gitweixin
  • 首页
  • 小程序代码
    • 资讯读书
    • 工具类
    • O2O
    • 地图定位
    • 社交
    • 行业软件
    • 电商类
    • 互联网类
    • 企业类
    • UI控件
  • 大数据开发
    • Hadoop
    • Spark
    • Hbase
    • Elasticsearch
    • Kafka
    • Flink
    • 数据仓库
    • 数据挖掘
    • flume
    • Kafka
    • Hive
    • shardingsphere
    • solr
  • 开发博客
    • Android
    • php
    • python
    • 运维
    • 技术架构
    • 数据库
  • 程序员网赚
  • bug清单
  • 量化投资
  • 在线查询工具
    • 去行号
    • 在线时间戳转换工具
    • 免费图片批量修改尺寸在线工具
    • SVG转JPG在线工具

分类归档Android

精品微信小程序开发门户,代码全部亲测可用

  • 首页   /  
  • 分类归档: "Android"
  • ( 页面4 )
Android, Harmony 6月 6,2021

鸿蒙Harmony和Android的UI控件概览对比

Android对于UI控件,可以在xml中来写ui控件, Android 提供对应 View 类及其子类的简明 XML 词汇,如用于微件和布局的词汇。也可使用 Android Studio 的 Layout Editor,并采用拖放界面来构建 XML 布局。 也可以直接在java等代码 创建 View 对象和 ViewGroup 对象(并操纵其属性) 。Harmony提供了Java UI框架和JS UI框架。两者的UI组件还是比较相同,都可以自定义组件。

Android的UI概览:

布局定义了应用中的界面结构(例如 Activity 的界面结构)。布局中的所有元素均使用 View 和 ViewGroup 对象的层次结构进行构建。View 通常用于绘制用户可看到并与之交互的内容。ViewGroup 则是不可见的容器,用于定义 View 和其他 ViewGroup 对象的布局结构

属性

每个 View 对象和 ViewGroup 对象均支持自己的各种 XML 属性。某些属性是 View 对象的特有属性(例如,TextView 支持 textSize 属性),但可扩展此类的任一 View 对象也会继承这些属性。某些属性是所有 View 对象的共有属性,因为它们继承自 View 根类(例如 id 属性)。此外,其他属性被视为“布局参数”,即描述 View 对象特定布局方向的属性,如该对象的父 ViewGroup 对象所定义的属性。

ID

任何 View 对象均可拥有与之关联的整型 ID,用于在结构树中对 View 对象进行唯一标识。编译应用后,系统会以整型形式引用此 ID,但在布局 XML 文件中,系统通常会以字符串的形式在 id 属性中指定该 ID。这是所有 View 对象共有的 XML 属性(由 View 类定义),并且您会经常使用该属性。XML 标记内部的 ID 语法是:

android:id="@+id/my_button"

字符串开头处的 @ 符号指示 XML 解析器应解析并展开 ID 字符串的其余部分,并将其标识为 ID 资源。加号 (+) 表示这是一个新的资源名称,必须创建该名称并将其添加到我们的资源(在 R.java 文件中)内。Android 框架还提供许多其他 ID 资源。引用 Android 资源 ID 时,不需要加号,但必须添加 android 软件包命名空间,如下所示:

android:id="@android:id/empty"

Harmony的Java UI概览:

应用的Ability在屏幕上将显示一个用户界面,该界面用来显示所有可被用户查看和交互的内容。

应用中所有的用户界面元素都是由Component和ComponentContainer对象构成。Component是绘制在屏幕上的一个对象,用户能与之交互。ComponentContainer是一个用于容纳其他Component和ComponentContainer对象的容器。

Java UI框架提供了一部分Component和ComponentContainer的具体子类,即创建用户界面(UI)的各类组件,包括一些常用的组件(比如:文本、按钮、图片、列表等)和常用的布局(比如:DirectionalLayout和DependentLayout)。用户可通过组件进行交互操作,并获得响应。

所有的UI操作都应该在主线程进行设置。

组件和布局

用户界面元素统称为组件,组件根据一定的层级结构进行组合形成布局。组件在未被添加到布局中时,既无法显示也无法交互,因此一个用户界面至少包含一个布局。在UI框架中,具体的布局类通常以XXLayout命名,完整的用户界面是一个布局,用户界面中的一部分也可以是一个布局。布局中容纳Component与ComponentContainer对象。

Component和ComponentContainer
  • Component:提供内容显示,是界面中所有组件的基类,开发者可以给Component设置事件处理回调来创建一个可交互的组件。Java UI框架提供了一些常用的界面元素,也可称之为组件,组件一般直接继承Component或它的子类,如Text、Image等。
  • ComponentContainer:作为容器容纳Component或ComponentContainer对象,并对它们进行布局。Java UI框架提供了一些标准布局功能的容器,它们继承自ComponentContainer,一般以“Layout”结尾,如DirectionalLayout、DependentLayout等。

图1 Component结构

LayoutConfig

每种布局都根据自身特点提供LayoutConfig供子Component设定布局属性和参数,通过指定布局属性可以对子Component在布局中的显示效果进行约束。例如:“width”、“height”是最基本的布局属性,它们指定了组件的大小。图2 LayoutConfig

组件树

布局把Component和ComponentContainer以树状的层级结构进行组织,这样的一个布局就称为组件树。组件树的特点是仅有一个根组件,其他组件有且仅有一个父节点,组件之间的关系受到父节点的规则约束。

Harmony JS UI框架概览:

JS UI框架是一种跨设备的高性能UI开发框架,支持声明式编程和跨设备多态UI。阅读本开发指南前,开发者需要掌握以下基础知识:

  • HTML5
  • CSS
  • JavaScript

关于组件和接口的详细参考文档请参见JS API参考。说明

本文档适用于手机(Phone)、平板(Tablet)、智慧屏(TV)和智能穿戴(Wearable)应用开发,针对轻量级智能穿戴(Lite Wearable)请参考轻量级智能穿戴开发。

基础能力
  • 声明式编程JS UI框架采用类HTML和CSS声明式编程语言作为页面布局和页面样式的开发语言,页面业务逻辑则支持ECMAScript规范的JavaScript语言。JS UI框架提供的声明式编程,可以让开发者避免编写UI状态切换的代码,视图配置信息更加直观。
  • 跨设备开发框架架构上支持UI跨设备显示能力,运行时自动映射到不同设备类型,开发者无感知,降低开发者多设备适配成本。
  • 高性能开发框架包含了许多核心的控件,如列表、图片和各类容器组件等,针对声明式语法进行了渲染流程的优化。
整体架构

JS UI框架包括应用层(Application)、前端框架层(Framework)、引擎层(Engine)和平台适配层(Porting Layer)。

  • Application应用层表示开发者使用JS UI框架开发的FA应用,这里的FA应用特指JS FA应用。使用Java开发FA应用请参考《Java UI框架》。
  • Framework前端框架层主要完成前端页面解析,以及提供MVVM(Model-View-ViewModel)开发模式、页面路由机制和自定义组件等能力。
  • Engine引擎层主要提供动画解析、DOM(Document Object Model)树构建、布局计算、渲染命令构建与绘制、事件管理等能力。
  • Porting Layer适配层主要完成对平台层进行抽象,提供抽象接口,可以对接到系统平台。比如:事件对接、渲染管线对接和系统生命周期对接等。

上一篇

作者 east
Android, Harmony 6月 6,2021

Harmony鸿蒙和Android安卓资源文件的分类对比

鸿蒙Harmoney的资源文件和Android很类似,下面做一些对比:

先来个总的概览:

Android的资源目录结构:

    res/
        drawable/
            graphic.png
        drawable-en/
        drawable-fr-rCA/
        drawable-en-port/
        drawable-en-notouch-12key/
        drawable-port-ldpi/
        drawable-port-notouch-12key/
        layout/
            main.xml
            info.xml
        mipmap/
            icon.png
        values/
            strings.xml
        drawable/

Harmoney的资源目录结构:

resources
|---base  // 默认存在的目录
|   |---element
|   |   |---string.json
|   |---media
|   |   |---icon.png
|---en_GB-vertical-car-mdpi // 限定词目录示例,需要开发者自行创建   
|   |---element
|   |   |---string.json
|   |---media
|   |   |---icon.png
|---rawfile  // 默认存在的目录

对比:目录结构差不多,都有存放图片、存放字符串的,都有配置限定字符,都是默认的目录;不同的是harmoney采用json,而不像android主要用xml来描述。

Android的限定词取值规则:

配置限定符值描述MCC 和 MNC示例:
mcc310
mcc310-mnc004
mcc208-mnc00
等等

移动设备国家代码 (MCC),(可选)后跟设备 SIM 卡中的移动设备网络代码 (MNC)。例如,mcc310 是指美国的任一运营商,mcc310-mnc004 是指美国的 Verizon 公司,mcc208-mnc00 是指法国的 Orange 公司。

如果设备使用无线装置连接(GSM 手机),则 MCC 和 MNC 值均来自 SIM 卡。

您也可以单独使用 MCC(例如,将国家/地区特定的合法资源加入应用)。如果只需根据语言指定,则改用语言和地区限定符(稍后进行介绍)。如果决定使用 MCC 和 MNC 限定符,请谨慎执行此操作并测试限定符是否按预期工作。

另请参阅配置字段 mcc 和 mnc,二者分别表示当前的移动设备国家代码和移动设备网络代码。语言和区域示例:
en
fr
en-rUS
fr-rFR
fr-rCA
b+en
b+en+US
b+es+419

语言通过由两个字母组成的 ISO 639-1 语言代码进行定义,可以选择后跟两个字母组成的 ISO 3166-1-alpha-2 区域码(前缀用小写字母 r)。

这些代码不区分大小写;r 前缀用于区分区域码。您不能单独指定区域。

Android 7.0(API 级别 24)引入对 BCP 47 语言标记的支持,可供您用来限定特定语言和区域的资源。语言标记由一个或多个子标记序列组成,每个子标记都能优化或缩小由整体标记标识的语言范围。如需了解有关语言标记的详细信息,请参阅用于标识语言的标记。

如要使用 BCP 47 语言标记,请将 b+ 和两个字母的 ISO 639-1 语言代码连接;其后还可选择使用其他子标记,用 + 分隔即可。

如果用户在系统设置中更改语言,则语言标记可能会在应用的生命周期中发生变更。如需了解运行时应用会因此受到何种影响,请参阅处理运行时变更。

有关针对其他语言本地化应用的完整指南,请参阅本地化。

另请参阅 getLocales() 方法,了解该方法提供的已定义语言区域列表。此列表包含主要的语言区域。布局方向ldrtl
ldltr

应用的布局方向。ldrtl 是指“布局方向从右到左”。ldltr 是指“布局方向从左到右”(默认的隐式值)。

此配置适用于布局、可绘制资源或值等任何资源。

例如,若要针对阿拉伯语提供某种特定布局,并针对任何其他“从右到左”的语言(如波斯语或希伯来语)提供某种通用布局,则可提供以下资源:

res/
    layout/
        main.xml (Default layout)
    layout-ar/
        main.xml (Specific layout for Arabic)
    layout-ldrtl/
        main.xml (Any "right-to-left" language, except
                  for Arabic, because the "ar" language qualifier
                  has a higher precedence.)

请注意:如要为应用启用从右到左的布局功能,则必须将 supportsRtl 设置为 "true",并将 targetSdkVersion 设置为 17 或更高版本。

此项为 API 级别 17 中的新增配置。smallestWidthsw<N>dp

示例:
sw320dp
sw600dp
sw720dp
等等

屏幕的基本尺寸,由可用屏幕区域的最小尺寸指定。具体而言,设备的 smallestWidth 是屏幕可用高度和宽度的最小尺寸(您也可将其视为屏幕的“最小可能宽度”)。无论屏幕的当前方向如何,您均可使用此限定符确保应用界面的可用宽度至少为 <N> dp。

例如,如果布局要求屏幕区域的最小尺寸始终至少为 600dp,则可使用此限定符创建布局资源 res/layout-sw600dp/。仅当可用屏幕的最小尺寸至少为 600dp(无论 600dp 表示的边是用户所认为的高度还是宽度)时,系统才会使用这些资源。最小宽度为设备的固定屏幕尺寸特征;即使屏幕方向发生变化,设备的最小宽度仍会保持不变。

使用最小宽度确定一般屏幕尺寸非常有用,因为宽度通常是设计布局时的驱动因素。界面经常会垂直滚动,但对其水平方向所需要的最小空间具有非常硬性的限制。可用宽度也是确定是否对手持式设备使用单窗格布局,或对平板电脑使用多窗格布局的关键因素。因此,您可能最关注每台设备上的最小可能宽度。

设备的最小宽度会将屏幕装饰元素和系统界面考虑在内。例如,如果设备屏幕上的某些永久性界面元素沿着最小宽度轴占据空间,则系统会声明最小宽度小于实际屏幕尺寸,因为这些屏幕像素不适用于您的界面。

以下是一些可用于常见屏幕尺寸的值:

  • 320,适用于屏幕配置如下的设备:
    • 240×320 ldpi(QVGA 手机)
    • 320×480 mdpi(手机)
    • 480×800 hdpi(高密度手机)
  • 480,适用于 480×800 mdpi 之类的屏幕(平板电脑/手机)。
  • 600,适用于 600×1024 mdpi 之类的屏幕(7 英寸平板电脑)。
  • 720,适用于 720×1280 mdpi 之类的屏幕(10 英寸平板电脑)。

当应用为多个资源目录提供不同的 smallestWidth 限定符值时,系统会使用最接近(但未超出)设备 smallestWidth 的值。

此项为 API 级别 13 中的新增配置。

另请参阅 android:requiresSmallestWidthDp 属性(声明与应用兼容的最小 smallestWidth)和 smallestScreenWidthDp 配置字段(存放设备的 smallestWidth 值)。

如需了解有关不同屏幕设计和使用此限定符的详细信息,请参阅支持多种屏幕开发者指南。可用宽度w<N>dp

示例:
w720dp
w1024dp
等等

指定资源应使用的最小可用屏幕宽度(以 dp 为单位,由 <N> 值定义)。当屏幕方向在横向和纵向之间切换时,此配置值也会随之变化,以匹配当前的实际宽度。

此功能往往有助于确定是否使用多窗格布局,因为即便在使用平板电脑设备时,您通常也不希望竖屏以横屏的方式使用多窗格布局。因此,您可以使用此功能指定布局所需的最小宽度,而无需同时使用屏幕尺寸和屏幕方向限定符。

应用为此配置提供具有不同值的多个资源目录时,系统会使用最接近(但未超出)设备当前屏幕宽度的值。此处的值会考虑屏幕装饰元素,因此如果设备显示屏的左边缘或右边缘上有一些永久性 UI 元素,考虑到这些 UI 元素,同时为减少应用的可用空间,设备会使用小于实际屏幕尺寸的宽度值。

此项为 API 级别 13 中的新增配置。

另请参阅 screenWidthDp 配置字段,该字段存放当前屏幕宽度。

如需了解有关不同屏幕设计和使用此限定符的详细信息,请参阅支持多种屏幕开发者指南。可用高度h<N>dp

示例:
h720dp
h1024dp
等等

指定资源应使用的最小可用屏幕高度(以“dp”为单位,由 <N> 值定义)。当屏幕方向在横向和纵向之间切换时,此配置值也会随之变化,以匹配当前的实际高度。

对比使用此方式定义布局所需高度与使用 w<N>dp 定义所需宽度,二者均非常有用,且都无需同时使用屏幕尺寸和方向限定符。但大多数应用不需要此限定符,因为界面经常垂直滚动,所以高度需更有弹性,而宽度则应更固定。

当应用为此配置提供具有不同值的多个资源目录时,系统会使用最接近(但未超出)设备当前屏幕高度的值。此处的值会考虑屏幕装饰元素,因此如果设备显示屏的上边缘或下边缘上有一些永久性 UI 元素,考虑到这些 UI 元素,同时为减少应用的可用空间,设备会使用小于实际屏幕尺寸的高度值。非固定的屏幕装饰元素(例如,全屏时可隐藏的手机状态栏)并不在考虑范围内,标题栏或操作栏等窗口装饰亦如此,因此应用必须准备好处理稍小于其指定值的空间。

此项为 API 级别 13 中的新增配置。

另请参阅 screenHeightDp 配置字段,该字段存放当前屏幕宽度。

如需了解有关不同屏幕设计和使用此限定符的详细信息,请参阅支持多种屏幕开发者指南。屏幕尺寸small
normal
large
xlarge

  • small:尺寸类似于低密度 VGA 屏幕的屏幕。小屏幕的最小布局尺寸约为 320×426 dp。例如,QVGA 低密度屏幕和 VGA 高密度屏幕。
  • normal:尺寸类似于中等密度 HVGA 屏幕的屏幕。标准屏幕的最小布局尺寸约为 320×470 dp。例如,WQVGA 低密度屏幕、HVGA 中等密度屏幕、WVGA 高密度屏幕。
  • large:尺寸类似于中等密度 VGA 屏幕的屏幕。大屏幕的最小布局尺寸约为 480×640 dp。例如,VGA 和 WVGA 中等密度屏幕。
  • xlarge:明显大于传统中等密度 HVGA 屏幕的屏幕。超大屏幕的最小布局尺寸约为 720×960 dp。在大多数情况下,屏幕超大的设备体积太大,不能放进口袋,最常见的是平板式设备。此项为 API 级别 9 中的新增配置。

请注意:使用尺寸限定符并不表示资源仅适用于该尺寸的屏幕。如果没有为备用资源提供最符合当前设备配置的限定符,则系统可能会使用其中最匹配的资源。

注意:如果所有资源均使用大于当前屏幕的尺寸限定符,则系统不会使用这些资源,并且应用将在运行时崩溃(例如,如果所有布局资源均以 xlarge 限定符标记,但设备是标准尺寸的屏幕)。

此项为 API 级别 4 中的新增配置。

如需了解详细信息,请参阅支持多种屏幕。

另请参阅 screenLayout 配置字段,该字段指示屏幕是小尺寸、标准尺寸还是大尺寸。屏幕纵横比long
notlong

  • long:宽屏,如 WQVGA、WVGA、FWVGA
  • notlong:非宽屏,如 QVGA、HVGA 和 VGA

此项为 API 级别 4 中新增配置。

此配置完全基于屏幕的纵横比(宽屏较宽),并且与屏幕方向无关。

另请参阅 screenLayout 配置字段,该字段指示屏幕是否为宽屏。圆形屏幕round
notround

  • round:圆形屏幕,例如圆形可穿戴式设备
  • notround:方形屏幕,例如手机或平板电脑

此项为 API 级别 23 中的新增配置。

另请参阅 isScreenRound() 配置方法,该方法指示屏幕是否为圆形屏幕。广色域widecg
nowidecg

  • {@code widecg}:显示广色域,如 Display P3 或 AdobeRGB
  • {@code nowidecg}:显示窄色域,如 sRGB

此项为 API 级别 26 中的新增配置。

另请参阅 isScreenWideColorGamut() 配置方法,该方法指示屏幕是否具有广色域。高动态范围 (HDR)highdr
lowdr

  • {@code highdr}:显示高动态范围
  • {@code lowdr}:显示低/标准动态范围

此项为 API 级别 26 中的新增配置。

另请参阅 isScreenHdr() 配置方法,该方法指示屏幕是否具有 HDR 功能。屏幕方向port
land

  • port:设备处于纵向(垂直)
  • land:设备处于横向状态(水平)

如果用户旋转屏幕,此配置可能会在应用生命周期中发生变化。如需了解这会在运行时期间给应用带来哪些影响,请参阅处理运行时变更。

另请参阅 orientation 配置字段,该字段指示当前的设备方向。界面模式car
desk
television
appliance
watch
vrheadset

  • car:设备正在车载手机座上显示
  • desk:设备正在桌面手机座上显示
  • television:设备正在通过电视显示内容,通过将界面投影到离用户较远的大屏幕上,为用户提供“十英尺”体验。主要面向遥控交互或其他非触控式交互
  • appliance:设备正在用作没有显示屏的装置
  • watch:设备配有显示屏,并且可戴在手腕上
  • vrheadset:设备正在通过虚拟现实耳机显示内容

此项为 API 级别 8 中的新增配置,API 13 中的新增电视配置,API 20 中的新增手表配置。

如需了解应用在设备插入基座或从中移除时的响应方式,请阅读确定并监控插接状态和类型。

如果用户将设备插入基座,此配置可能会在应用生命周期中发生变化。您可以使用 UiModeManager 启用或禁用其中的部分模式。如需了解这会在运行时期间给应用带来哪些影响,请参阅处理运行时变更。夜间模式night
notnight

  • night:夜间
  • notnight:白天

此项为 API 级别 8 中的新增配置。

如果夜间模式停留在自动模式(默认),此配置可能会在应用生命周期中发生变化。在此情况下,该模式会根据当天的时间进行调整。您可以使用 UiModeManager 启用或禁用此模式。如需了解这会在运行时期间给应用带来哪些影响,请参阅处理运行时变更。屏幕像素密度 (dpi)ldpi
mdpi
hdpi
xhdpi
xxhdpi
xxxhdpi
nodpi
tvdpi
anydpi
nnndpi

  • ldpi:低密度屏幕;约为 120dpi。
  • mdpi:中等密度(传统 HVGA)屏幕;约为 160dpi。
  • hdpi:高密度屏幕;约为 240dpi。
  • xhdpi:超高密度屏幕;约为 320dpi。此项为 API 级别 8 中的新增配置
  • xxhdpi:绝高密度屏幕;约为 480dpi。此项为 API 级别 16 中的新增配置
  • xxxhdpi:极高密度屏幕使用(仅限启动器图标,请参阅支持多种屏幕中的注释);约为 640dpi。此项为 API 级别 18 中的新增配置
  • nodpi:可用于您不希望为匹配设备密度而进行缩放的位图资源。
  • tvdpi:密度介于 mdpi 和 hdpi 之间的屏幕;约为 213dpi。此限定符并非指“基本”密度的屏幕。它主要用于电视,且大多数应用都不使用该密度 — 大多数应用只会使用 mdpi 和 hdpi 资源,而且系统将根据需要对这些资源进行缩放。此项为 API 级别 13 中的新增配置
  • anydpi:此限定符适合所有屏幕密度,其优先级高于其他限定符。这非常适用于矢量可绘制对象。此项为 API 级别 21 中的新增配置
  • nnndpi:用于表示非标准密度,其中 nnn 是正整数屏幕密度。此限定符不适用于大多数情况。使用标准密度存储分区,可显著减少因支持市场上各种设备屏幕密度而产生的开销。

六个基本密度之间的缩放比为 3:4:6:8:12:16(忽略 tvdpi 密度)。因此,9×9 (ldpi) 位图相当于 12×12 (mdpi)、18×18 (hdpi)、24×24 (xhdpi) 位图,依此类推。

如果您认为图像资源在电视或其他某些设备上的呈现效果不够好,进而想尝试使用 tvdpi 资源,则缩放系数应为 1.33*mdpi。例如,mdpi 屏幕的 100px x 100px 图像应相当于 tvdpi 屏幕的 133px x 133px 图像。

请注意:使用密度限定符并不表示资源仅适用于该密度的屏幕。如果没有为备用资源提供最符合当前设备配置的限定符,则系统可能使用其中最匹配的资源。

如需详细了解如何处理不同屏幕密度以及 Android 如何缩放位图以适应当前密度,请参阅支持多种屏幕。触摸屏类型notouch
finger

  • notouch:设备没有触摸屏。
  • finger:设备有一个专供用户通过手指直接进行交互的触摸屏。

另请参阅 touchscreen 配置字段,该字段指示设备上的触摸屏类型。键盘可用性keysexposed
keyshidden
keyssoft

  • keysexposed:设备拥有可用的键盘。如果设备启用了软键盘(不无可能),那么即使用户未找到硬键盘,或者该设备没有硬键盘,也可使用此限定符。如果未提供或已禁用软键盘,则只有在配备硬键盘的情况下才可使用此限定符。
  • keyshidden:设备具有可用的硬键盘,但其处于隐藏状态,且设备未启用软键盘。
  • keyssoft:设备已启用软键盘(无论是否可见)。

如果您提供了 keysexposed 资源,但未提供 keyssoft 资源,则无论键盘是否可见,只要系统已启用软键盘,其便会使用 keysexposed 资源。

如果用户打开硬键盘,此配置可能会在应用生命周期中发生变化。如需了解这会在运行时期间给应用带来哪些影响,请参阅处理运行时变更。

另请参阅配置字段 hardKeyboardHidden 和 keyboardHidden,二者分别指示硬键盘的可见性和任一键盘(包括软键盘)的可见性。主要的文本输入法nokeys
qwerty
12key

  • nokeys:设备没有用于文本输入的硬按键。
  • qwerty:设备拥有标准硬键盘(无论是否对用户可见)。
  • 12key:设备拥有 12 键硬键盘(无论是否对用户可见)。

另请参阅 keyboard 配置字段,该字段指示可用的主要文本输入法。导航键可用性navexposed
navhidden

  • navexposed:导航键可供用户使用。
  • navhidden:导航键不可用(例如,在密封盖子后面)。

如果用户显示导航键,此配置可能会在应用生命周期中发生变化。如需了解这会在运行时期间给应用带来哪些影响,请参阅处理运行时变更。

另请参阅 navigationHidden 配置字段,该字段指示导航键是否处于隐藏状态。主要的非触摸导航方法nonav
dpad
trackball
wheel

  • nonav:除了使用触摸屏以外,设备没有其他导航设施。
  • dpad:设备具有用于导航的方向键。
  • trackball:设备具有用于导航的轨迹球。
  • wheel:设备具有用于导航的方向盘(不常见)。

另请参阅 navigation 配置字段,该字段指示可用的导航方法类型。平台版本(API 级别)示例:
v3
v4
v7
等等

设备支持的 API 级别。例如,v1 对应 API 级别 1(装有 Android 1.0 或更高版本系统的设备),v4 对应 API 级别 4(装有 Android 1.6 或更高版本系统的设备)。如需了解有关这些值的详细信息,请参阅 Android API 级别文档。

Harmony的限定词取值规则:

限定词类型含义与取值说明
移动国家码和移动网络码移动国家码(MCC)和移动网络码(MNC)的值取自设备注册的网络。MCC后面可以跟随MNC,使用下划线(_)连接,也可以单独使用。例如:mcc460表示中国,mcc460_mnc00表示中国_中国移动。详细取值范围,请查阅ITU-T E.212(国际电联相关标准)。
语言表示设备使用的语言类型,由2~3个小写字母组成。例如:zh表示中文,en表示英语,mai表示迈蒂利语。详细取值范围,请查阅ISO 639(ISO制定的语言编码标准)。
文字表示设备使用的文字类型,由1个大写字母(首字母)和3个小写字母组成。例如:Hans表示简体中文,Hant表示繁体中文。详细取值范围,请查阅ISO 15924(ISO制定的文字编码标准)。
国家或地区表示用户所在的国家或地区,由2~3个大写字母或者3个数字组成。例如:CN表示中国,GB表示英国。详细取值范围,请查阅ISO 3166-1(ISO制定的国家和地区编码标准)。
横竖屏表示设备的屏幕方向,取值如下:vertical:竖屏horizontal:横屏
设备类型表示设备的类型,取值如下:phone:手机tablet:平板car:车机tv:智慧屏wearable:智能穿戴
颜色模式表示设备的颜色模式,取值如下:dark:深色模式light:浅色模式
屏幕密度表示设备的屏幕密度(单位为dpi),取值如下:sdpi:表示小规模的屏幕密度(Small-scale Dots Per Inch),适用于dpi取值为(0, 120]的设备。mdpi:表示中规模的屏幕密度(Medium-scale Dots Per Inch),适用于dpi取值为(120, 160]的设备。ldpi:表示大规模的屏幕密度(Large-scale Dots Per Inch),适用于dpi取值为(160, 240]的设备。xldpi:表示特大规模的屏幕密度(Extra Large-scale Dots Per Inch),适用于dpi取值为(240, 320]的设备。xxldpi:表示超大规模的屏幕密度(Extra Extra Large-scale Dots Per Inch),适用于dpi取值为(320, 480]的设备。xxxldpi:表示超特大规模的屏幕密度(Extra Extra Extra Large-scale Dots Per Inch),适用于dpi取值为(480, 640]的设备。

对比说明:

还是很类似,比如语言、横竖屏,android发展年数比较久,规则和完档相对多些。而harmony强大之处是支持万物互联,比android多了一些支持设备。

作者 east
Android 2月 11,2021

Android自定义采颜色对话框

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.graphics.SweepGradient;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;

@SuppressLint({ “DrawAllocation” })
public class ColorPickerDialog extends Dialog {
private int mInitialColor;
private OnColorChangedListener mListener;

public ColorPickerDialog(Context paramContext,
        OnColorChangedListener paramOnColorChangedListener, int paramInt) {
    super(paramContext);
    this.mListener = paramOnColorChangedListener;
    this.mInitialColor = paramInt;
}

protected void onCreate(Bundle paramBundle) {
    super.onCreate(paramBundle);
    mListener = new OnColorChangedListener() {
        public void colorChanged(int paramAnonymousInt) {
            ColorPickerDialog.this.mListener
                    .colorChanged(paramAnonymousInt);
            ColorPickerDialog.this.dismiss();
        }
    };
    setContentView(new ColorPickerView(getContext(), mListener,
            this.mInitialColor));
    setTitle("Pick a Color");
}

private static class ColorPickerView extends View {
    private static final int CENTER_RADIUS = 32;
    private static int CENTER_X = 120;
    private static int CENTER_Y = 120;
    private static final float PI = 3.1415925F;
    private Paint mCenterPaint;
    private final int[] mColors;
    private boolean mHighlightCenter;
    private ColorPickerDialog.OnColorChangedListener mListener;
    private Paint mPaint;
    private boolean mTrackingCenter;

    ColorPickerView(
            Context paramContext,
            ColorPickerDialog.OnColorChangedListener paramOnColorChangedListener,
            int paramInt) {
        super(paramContext);
        this.mListener = paramOnColorChangedListener;
        this.mColors = new int[] { -65536, -65281, -16776961, -16711681,
                -16711936, 65280, -65536 };
        SweepGradient gradient = new SweepGradient(0.0F, 0.0F,
                this.mColors, null);
        this.mPaint = new Paint(1);
        this.mPaint.setShader(gradient);
        this.mPaint.setStyle(Paint.Style.STROKE);
        this.mPaint.setStrokeWidth(32.0F);
        this.mCenterPaint = new Paint(1);
        this.mCenterPaint.setColor(paramInt);
        this.mCenterPaint.setStrokeWidth(5.0F);
    }

    private int ave(int paramInt1, int paramInt2, float paramFloat) {
        return Math.round((paramInt2 - paramInt1) * paramFloat) + paramInt1;
    }

    private int interpColor(int[] paramArrayOfInt, float paramFloat) {
        if (paramFloat <= 0.0F) {
            return paramArrayOfInt[0];
        }
        if (paramFloat >= 1.0F) {
            return paramArrayOfInt[(paramArrayOfInt.length - 1)];
        }
        paramFloat *= (paramArrayOfInt.length - 1);
        int j = (int) paramFloat;
        paramFloat -= j;
        int i = paramArrayOfInt[j];
        j = paramArrayOfInt[(j + 1)];
        return Color.argb(ave(Color.alpha(i), Color.alpha(j), paramFloat),
                ave(Color.red(i), Color.red(j), paramFloat),
                ave(Color.green(i), Color.green(j), paramFloat),
                ave(Color.blue(i), Color.blue(j), paramFloat));
    }

    protected void onDraw(Canvas paramCanvas) {
        float f = CENTER_X - this.mPaint.getStrokeWidth() * 0.5F;
        paramCanvas.translate(CENTER_X, CENTER_X);
        paramCanvas.drawOval(new RectF(-f, -f, f, f), this.mPaint);
        paramCanvas.drawCircle(0.0F, 0.0F, 32.0F, this.mCenterPaint);
        int i;
        if (this.mTrackingCenter) {
            i = this.mCenterPaint.getColor();
            this.mCenterPaint.setStyle(Paint.Style.STROKE);
            if (this.mHighlightCenter) {
                this.mCenterPaint.setAlpha(255);
            } else {
                this.mCenterPaint.setAlpha(128);
            }
            paramCanvas.drawCircle(0.0F, 0.0F,
                    this.mCenterPaint.getStrokeWidth() + 32.0F,
                    this.mCenterPaint);
            this.mCenterPaint.setStyle(Paint.Style.FILL);
            this.mCenterPaint.setColor(i);

        }
    }

    protected void onMeasure(int paramInt1, int paramInt2) {
        setMeasuredDimension(CENTER_X * 2, CENTER_X * 2);
    }

    public boolean onTouchEvent(MotionEvent paramMotionEvent) {
        float f1 = paramMotionEvent.getX() - CENTER_X;
        float f2 = paramMotionEvent.getY() - CENTER_Y;
        boolean bool;
        if (Math.sqrt(f1 * f1 + f2 * f2) <= 32.0D) {
            bool = true;
        } else {
            bool = false;
        }
        switch (paramMotionEvent.getAction()) {
        case 0:
            return true;

        case 1:

            if (mTrackingCenter) {
                if (mTrackingCenter != bool) {
                    this.mHighlightCenter = bool;
                    invalidate();
                    return true;
                } else {
                    f2 = (float) Math.atan2(f2, f1) / 6.283185F;
                    f1 = f2;
                    if (f2 < 0.0F) {
                        f1 = f2 + 1.0F;
                    }
                    this.mCenterPaint
                            .setColor(interpColor(this.mColors, f1));
                    invalidate();
                    return true;
                }
            }
            break;

        case 2:

            if (mTrackingCenter) {
                if (bool) {
                    this.mListener.colorChanged(this.mCenterPaint
                            .getColor());
                }
                this.mTrackingCenter = false;
                invalidate();
                return true;
            }
        }

        return false;
    }
}

public static abstract interface OnColorChangedListener {
    public abstract void colorChanged(int paramInt);
}

}

作者 east
Android 2月 11,2021

Android手电筒后台Service

功能是后台service控制手电的打开和关闭。

public class ToggleFlashService extends Service {
	final String SMS_RECEIVED_STRING = "android.provider.Telephony.SMS_RECEIVED";
	AudioManager audioManager;
	int callState;
	int flashDuration;
	int flashInterval;
	boolean isCameraInUse = false;
	boolean isCameraOpen = false;
	boolean isFlashOn = false;
	Camera.Parameters params;
	Intent serviceIntent;
	TelephonyManager telephonyManager;
	Timer timer;
	TimerTask timerTask;

	public boolean isCameraInUse() {
		try {
			if ((ToggleFlashSettings.camera == null) && (!this.isCameraOpen)) {
				ToggleFlashSettings.camera = Camera.open();
				this.params = ToggleFlashSettings.camera.getParameters();
				this.isCameraOpen = true;
				return false;
			}			
		} catch (Exception localException) {
		}
		return true;
	}

	public void messageReceived() {
	}

	public IBinder onBind(Intent paramIntent) {
		return null;
	}

	public void onCreate() {
		this.telephonyManager = ((TelephonyManager) getSystemService("phone"));
		this.audioManager = ((AudioManager) getSystemService("audio"));
		this.callState = this.telephonyManager.getCallState();
		this.flashDuration = ToggleFlashPrefs
				.getCallFlashInterval(getApplicationContext());
		this.flashInterval = ToggleFlashPrefs
				.getCallFlashDuration(getApplicationContext());
		super.onCreate();
	}


	public void onDestroy() {
		Log.e("Toggle", "ToggleFlash Destroy");
		try {
			if (this.timer != null) {
				this.timer.cancel();
				this.timer = null;
			}
			if ((this.isFlashOn) && (ToggleFlashSettings.camera != null)) {
				this.isFlashOn = false;
				this.isCameraOpen = false;
				this.params = ToggleFlashSettings.camera.getParameters();
				List localList = this.params.getSupportedFlashModes();
				if ((localList.contains("torch"))
						|| (localList.contains("off"))
						|| (localList.contains("on"))) {
					this.params.setFlashMode("off");
					ToggleFlashSettings.camera.setParameters(this.params);
				}
				ToggleFlashSettings.camera.release();
				ToggleFlashSettings.camera = null;
			}			
		} catch (Exception localException) {
			if (ToggleFlashSettings.camera != null) {
				ToggleFlashSettings.camera.release();
				ToggleFlashSettings.camera = null;
			}
			localException.printStackTrace();
		}
	}


	public int onStartCommand(Intent paramIntent, int paramInt1, int paramInt2) {
		if (ToggleFlashSettings.camera != null) {
			ToggleFlashSettings.camera.release();
			ToggleFlashSettings.camera = null;
		}
		this.serviceIntent = paramIntent;
		this.telephonyManager.listen(new PhoneStateListener() {
			public void onCallStateChanged(int paramAnonymousInt,
					String paramAnonymousString) {
				switch (paramAnonymousInt) {
				default:
					ToggleFlashService.this.callState = 0;
					break;
				case 1:
					ToggleFlashService.this.callState = 1;
					break;
				case 2:
					ToggleFlashService.this.callState = 2;
					break;
				}

			}
		}, 32);
		this.timer = new Timer();
		this.timerTask = new TimerTask() {
			public void run() {
				ToggleFlashService.this.toggleFlash();
			}
		};
		if (this.flashDuration == 0) {
			this.timer.schedule(this.timerTask, this.flashInterval, 50L);
		} else {
			this.timer.schedule(this.timerTask, this.flashInterval,
					this.flashDuration);
		}
		return super.onStartCommand(paramIntent, paramInt1, paramInt2);
	}


	public boolean stopService(Intent paramIntent) {
		try {
			if (this.timer != null) {
				this.timer.cancel();
				this.timer = null;
			}
			if ((this.isFlashOn) && (ToggleFlashSettings.camera != null)) {
				this.isFlashOn = false;
				this.isCameraOpen = false;
				this.params = ToggleFlashSettings.camera.getParameters();
				List localList = this.params.getSupportedFlashModes();
				if ((localList.contains("torch"))
						|| (localList.contains("off"))
						|| (localList.contains("on"))) {
					this.params.setFlashMode("off");
					ToggleFlashSettings.camera.setParameters(this.params);
				}
				ToggleFlashSettings.camera.release();
				ToggleFlashSettings.camera = null;
			}
			this.isCameraOpen = false;
			this.isFlashOn = false;
			if (ToggleFlashSettings.camera != null) {
				ToggleFlashSettings.camera.release();
				ToggleFlashSettings.camera = null;
			}
			if (this.serviceIntent != null) {
				boolean bool = super.stopService(this.serviceIntent);
				return bool;
			}
		} catch (Exception localException) {
			localException.printStackTrace();
			if (ToggleFlashSettings.camera != null) {
				ToggleFlashSettings.camera.release();
				ToggleFlashSettings.camera = null;
			}
			ToggleFlashSettings.camera = null;
		}
		return super.stopService(paramIntent);
	}

	
	public void toggleFlash() {		
		try {
			this.isCameraInUse = isCameraInUse();
			if (this.isCameraInUse) {
				stopService(this.serviceIntent);
				Log.e("ToggleFlashServer", "is isCameraInUse ");     
				return;
			}
			if (this.callState != 1) {
				Log.e("ToggleFlashServer", "callState is 1 ");     
				return;
			}
			 List localList = this.params.getSupportedFlashModes();
			if (!this.isFlashOn) {
				if ((localList.contains("torch"))
						|| (localList.contains("off"))
						|| (localList.contains("on"))) {
					Log.e("ToggleFlashServer", "start torch.... ");    
					this.params.setFlashMode("torch");
					if (ToggleFlashSettings.camera != null) {
						ToggleFlashSettings.camera.setParameters(this.params);
						ToggleFlashSettings.camera.startPreview();
					}
				}
				this.isFlashOn = true;
				return;
			}
			
			if ((localList.contains("torch")) || (localList.contains("off"))
					|| (localList.contains("on"))) {
				this.params.setFlashMode("off");
				if (ToggleFlashSettings.camera != null) {
					ToggleFlashSettings.camera.setParameters(this.params);
					ToggleFlashSettings.camera.stopPreview();
				}
			}
			this.isFlashOn = false;
			stopService(this.serviceIntent);
			
		} catch (Exception localException) {
			localException.printStackTrace();

		}
		
	}
}

public class ToggleFlashPrefs {
	public static final String CALL_FLASH_DURATION = "call_flash_duration";
	public static final String CALL_FLASH_INTERVAL = "call_flash_interval";
	public static final String MSG_FLASH_DURATION = "msg_flash_duration";
	public static final String MSG_FLASH_INTERVAL = "msg_flash_interval";
	public static final String SERVICE_STARTED = "service_started";
	public static final String SET_ALARM_ENABLE = "set_alarm_enable";
	public static final String SET_CALL_ENABLE = "set_call_enable";
	public static final String SET_MESSAGE_ENABLE = "set_message_enable";
	public static final String SET_NOTIFICATION_ENABLE = "set_notification_enable";

	public static boolean getAlarmEnable(Context paramContext) {
		return getSharedPreferences(paramContext).getBoolean(
				"set_alarm_enable", false);
	}

	public static boolean getCallEnable(Context paramContext) {
		return getSharedPreferences(paramContext).getBoolean("set_call_enable",
				true);
	}

	public static int getCallFlashDuration(Context paramContext) {
		return getSharedPreferences(paramContext).getInt("call_flash_duration",
				250);
	}

	public static int getCallFlashInterval(Context paramContext) {
		return getSharedPreferences(paramContext).getInt("call_flash_interval",
				0);
	}

	public static int getMSGFlashDuration(Context paramContext) {
		return getSharedPreferences(paramContext).getInt("msg_flash_duration",
				250);
	}

	public static int getMSGFlashInterval(Context paramContext) {
		return getSharedPreferences(paramContext).getInt("msg_flash_interval",
				0);
	}

	public static boolean getMessageEnable(Context paramContext) {
		return getSharedPreferences(paramContext).getBoolean(
				"set_message_enable", true);
	}

	public static boolean getNotificationEnable(Context paramContext) {
		return getSharedPreferences(paramContext).getBoolean(
				"set_notification_enable", false);
	}

	public static boolean getServiceStarted(Context paramContext) {
		return getSharedPreferences(paramContext).getBoolean("service_started",
				false);
	}

	static SharedPreferences getSharedPreferences(Context paramContext) {
		return PreferenceManager.getDefaultSharedPreferences(paramContext);
	}

	public static void setAlarmEnable(Context paramContext, boolean paramBoolean) {
		SharedPreferences.Editor editor = getSharedPreferences(paramContext)
				.edit();
		editor.putBoolean("set_alarm_enable", paramBoolean);
		editor.commit();
	}

	public static void setCallEnable(Context paramContext, boolean paramBoolean) {
		SharedPreferences.Editor editor = getSharedPreferences(paramContext)
				.edit();
		editor.putBoolean("set_call_enable", paramBoolean);
		editor.commit();
	}

	public static void setCallFlashDuration(Context paramContext, int paramInt) {
		SharedPreferences.Editor editor = getSharedPreferences(paramContext)
				.edit();
		editor.putInt("call_flash_duration", paramInt);
		editor.commit();
	}

	public static void setCallFlashInterval(Context paramContext, int paramInt) {
		SharedPreferences.Editor editor = getSharedPreferences(paramContext)
				.edit();
		editor.putInt("call_flash_interval", paramInt);
		editor.commit();
	}

	public static void setMSGFlashDuration(Context paramContext, int paramInt) {
		SharedPreferences.Editor editor = getSharedPreferences(paramContext)
				.edit();
		editor.putInt("msg_flash_duration", paramInt);
		editor.commit();
	}

	public static void setMSGFlashInterval(Context paramContext, int paramInt) {
		SharedPreferences.Editor editor = getSharedPreferences(paramContext)
				.edit();
		editor.putInt("msg_flash_interval", paramInt);
		editor.commit();
	}

	public static void setMessageEnable(Context paramContext,
			boolean paramBoolean) {
		SharedPreferences.Editor editor = getSharedPreferences(paramContext)
				.edit();
		editor.putBoolean("set_message_enable", paramBoolean);
		editor.commit();
	}

	public static void setNotificationEnable(Context paramContext,
			boolean paramBoolean) {
		SharedPreferences.Editor editor = getSharedPreferences(paramContext)
				.edit();
		editor.putBoolean("set_notification_enable", paramBoolean);
		editor.commit();
	}

	public static void setServiceStarted(Context paramContext,
			boolean paramBoolean) {
		SharedPreferences.Editor editor = getSharedPreferences(paramContext)
				.edit();
		editor.putBoolean("service_started", paramBoolean);
		editor.commit();
	}
}
作者 east
Android 2月 11,2021

Android单行文本跑马灯控件

public class AutoScrollTextView extends TextView implements OnClickListener {
public final static String TAG = AutoScrollTextView.class.getSimpleName();
private int runNum = 1;// 滚动次数
private float textLength = 0f;// 文本长度
private float viewWidth = 0f;
private float step = 0f;// 文字的横坐标
private float y = 0f;// 文字的纵坐标
private float temp_view_plus_text_length = 0.0f;// 用于计算的临时变量
private float temp_view_plus_two_text_length = 0.0f;// 用于计算的临时变量
public boolean isStarting = false;// 是否开始滚动
private Paint paint = null;// 绘图样式
private Paint paint1 = null;// 绘图样式
private String text = “”;// 文本内容
private boolean isFade = true;// 是否加影子
private float layout_width = 168f;

public float getLayout_width() {
    return layout_width;
}

public void setLayout_width(float layoutWidth) {
    layout_width = layoutWidth;
}

public boolean isFade() {
    return isFade;
}

public void setFade(boolean isFade) {
    this.isFade = isFade;
}

public AutoScrollTextView(Context context) {
    super(context);
    initView();
}

public AutoScrollTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView();
}

public AutoScrollTextView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    initView();
}

/** */
/**
 * 初始化控件
 */
private void initView() {
    setOnClickListener(this);
}

/** */
/**
 * 文本初始化,每次更改文本内容或者文本效果等之后都需要重新初始化一下
 */
public void init(WindowManager windowManager) {
    paint = getPaint();
    paint1 = new Paint();
    paint1.setColor(Color.WHITE);
    paint1.setTextSize(getTextSize());
    text = getText().toString();
    textLength = paint.measureText(text);
    viewWidth = layout_width * LogoActivity.screenHeight / 960;
    if (textLength > viewWidth * 2) {
        isStarting = true;
    }
    Log.e("    textLength      ", textLength + "         " + viewWidth);
    if (viewWidth == 0) {
        if (windowManager != null) {
            Display display = windowManager.getDefaultDisplay();
            viewWidth = display.getWidth();
        }
    }
    temp_view_plus_text_length = viewWidth + textLength;
    temp_view_plus_two_text_length = viewWidth + textLength * 2;
    step = temp_view_plus_text_length;
    y = getTextSize() + getPaddingTop();
}

@Override
public Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();
    SavedState ss = new SavedState(superState);

    ss.step = step;
    ss.isStarting = isStarting;

    return ss;

}

@Override
public void onRestoreInstanceState(Parcelable state) {
    if (!(state instanceof SavedState)) {
        super.onRestoreInstanceState(state);
        return;
    }
    SavedState ss = (SavedState) state;
    super.onRestoreInstanceState(ss.getSuperState());

    step = ss.step;
    isStarting = ss.isStarting;

}

public static class SavedState extends BaseSavedState {
    public boolean isStarting = false;
    public float step = 0.0f;

    SavedState(Parcelable superState) {
        super(superState);
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        out.writeBooleanArray(new boolean[] { isStarting });
        out.writeFloat(step);
    }

    public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {

        public SavedState[] newArray(int size) {
            return new SavedState[size];
        }

        @Override
        public SavedState createFromParcel(Parcel in) {
            return new SavedState(in);
        }
    };

    private SavedState(Parcel in) {
        super(in);
        boolean[] b = null;
        in.readBooleanArray(b);
        if (b != null && b.length > 0)
            isStarting = b[0];
        step = in.readFloat();
    }
}

/** */
/**
 * 开始滚动
 */
public void startScroll() {
    isStarting = true;
    invalidate();
}

/** */
/**
 * 停止滚动
 */
public void stopScroll() {
    isStarting = false;
    runNum = 1;
    invalidate();

}

@Override
public void onDraw(Canvas canvas) {
    if (isFade)canvas.drawText(text, temp_view_plus_text_length - step, y, paint1);
    canvas.drawText(text, temp_view_plus_text_length - step, y - 1, paint);
    if (!isStarting) {
        return;
    }
    step += 1;
    if (step > temp_view_plus_two_text_length) {
        step = textLength - viewWidth;
        runNum--;

    }
    if (runNum == 0 && step == temp_view_plus_text_length) {
        isStarting = false;
    }
    invalidate();

}

@Override
public void onClick(View v) {

// if (isStarting)
// stopScroll();
// else
// startScroll();

}

}

作者 east
Android 2月 11,2021

Android游戏加载资源通用类

开发游戏,通常需要在Assets中加载图片和多语言包,下面有个封装类。

/**
 * 图片加载类.
 * 
 * @author 凉
 * 
 */
public class Assets {
	private static Map<String, Map<String, String>> drawables = new HashMap<String, Map<String, String>>();

	private static Map<String, String> raws = new HashMap<String, String>();

	/**
	 * 如果是中文,则从drawable-zh目录加载图片.如果没有,在从drawable里面加载.
	 * 
	 * @param name
	 * @return
	 */
	public static String get(String name) {
		String result = null;
		if (isLunarSetting()) {
			Map map = (Map) drawables.get("drawable-zh");
			if (map != null) {
				String str3 = get(map, name);
				MyLog.d("", ">>>>>>>>>>>path>>>cn>>drawables:" + str3
						+ "   name:" + name);
				if (str3 != null) {
					result = str3;
				} else {
					String str4 = get((Map) drawables.get("drawable"), name);
					MyLog.d("", ">>>>>>>>>>>path>>>en>1>drawables:" + str4
							+ "   name:" + name);
					if (str4 != null) {
						result = str4;
					}
				}
			} else {
				String str2 = get((Map) drawables.get("drawable"), name);
				MyLog.d("", ">>>>>>>>>>>path>>>en>2>drawables:" + str2
						+ "   name:" + name);
				if (str2 != null) {
					result = str2;
				}
			}
		} else {
			String str1 = get((Map) drawables.get("drawable"), name);
			MyLog.d("", ">>>>>>>>>>>path>>>en>3>drawables:" + str1 + "   name:"
					+ name);
			if (str1 != null) {
				result = str1;
			}
		}
		return result;
	}

	private static String get(Map<String, String> map, String name) {
		String str = null;
		if (map.get(name + ".png") != null)
			str = (String) map.get(name + ".png");
		else {
			if (map.get(name + ".jpg") != null) {
				str = (String) map.get(name + ".jpg");
			}
		}
		return str;
	}

	private static String getLanguageEnv() {
		Locale local = Locale.getDefault();
		String language = local.getLanguage();
		String country = local.getCountry().toLowerCase();
		if ("zh".equals(language)) {
			if ("cn".equals(country))
				language = "zh-CN";
			else if ("tw".equals(country)) {
				language = "zh-TW";
			}
		} else if ("pt".equals(language)) {
			if ("br".equals(country)) {
				language = "pt-BR";
			} else if ("pt".equals(country)) {
				language = "pt-PT";
			}
		}
		return language;
	}

	/**
	 * 获取.plist资源.
	 * 
	 * @param name
	 * @return
	 */
	public static String getRaw(String name) {
		return (String) raws.get(name + ".plist");
	}

	/**
	 * 把文件夹res下面的drawable和raw下面的文件路径全部缓存起来.
	 * 
	 * @param context
	 * @throws IOException
	 */
	public static void init(Context context) throws IOException {
		String[] arraystr = context.getAssets().list("res");
		int i = arraystr.length;
		for (int j = 0; j < i; j++) {
			String str1 = arraystr[j];
			String[] strings;
			HashMap drawable;
			if (str1.startsWith("drawable")) {
				strings = context.getAssets().list("res/" + str1);
				drawable = new HashMap();
				int n = strings.length;
				for (int i1 = 0; i1 < n; i1++) {
					String str3 = strings[i1];
					drawable.put(str3, "res/" + str1 + "/" + str3);
				}
				drawables.put(str1, drawable);
			} else if (str1.equals("raw")) {
				String[] lists = context.getAssets().list("res/" + str1);
				for (String str2 : lists)
					raws.put(str2, "res/" + str1 + "/" + str2);
			}
		}
	}

	/**
	 * 是否是中文.
	 * 
	 * @return
	 */
	public static boolean isLunarSetting() {
		String str = getLanguageEnv();
		return ((str != null) && ((str.trim().equals("zh-CN")) || (str.trim()
				.equals("zh-TW"))));
	}
}
作者 east
Android, bug清单 1月 5,2021

Android解决Only the original thread that created a view hierarchy can touch its views问题

 
比如在电话的应用程序中,有一个线程是RingTone,如果这个非UI线程中直接操作UI线程,会抛出android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views: 
   1. Why
       为什么会有这种异常产生呢?
       
      当每个应用程序apk第一次启动时,Android会同时启动一个对应的主线程(Main Thread),
      主线程负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,
      并把相关的事件分发到对应的组件进行处理,所以主线程通常又被叫做UI线程。


      但是在开发Android应用时必须遵守单线程模型的原则: 
      Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行,如果在非UI线程中直接操作UI线程,
      会抛出android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that 
      created a view hierarchy can touch its views,这与普通的java程序不同。


       由于UI线程负责事件的监听和绘图,因此,必须保证UI线程能够随时响应用户的需求,
       UI线程里的操作应该向中断事件那样短小,费时的操作(如网络连接)需要另开线程,
       否则,如果UI线程超过5s没有响应用户请求,会弹出对话框提醒用户终止应用程序(ANP)。


       如果在新开的线程中需要对UI进行设定,就可能违反单线程模型,
       因此android采用一种的Message Queue机制保证线程间通信。


       Message Queue是一个消息队列,用来存放通过Handler发送的消息。
       Android在第一启动程序时会默认会为UI thread创建一个关联的消息队列,
      可以通过Looper.myQueue()得到当前线程的消息队列,用来管理程序的一些上层组件,
      activities,broadcast receivers 等,你可以在自己的子线程中创建Handler与UI thread通讯。


      Handler会向message queue通过两种方法发送消息:send或post。
      这两种消息都会插在message queue队尾并按先进先出执行,
      但通过这两种方法发送的消息执行的方式略有不同:
      1)通过send发送的是一个message对象, 会被handler的 handleMessage()函数处理;
      2)而通过post方法发送的是一个runnable对象,则会自己执行。


      每个带图形界面的应用启动后,都会创建一个主线程,可称之为UI线程。
      这个线程自动就会创建一个message queue,来自于系统的消息都会投放到这个message queue里面,
      并按先进先出的顺序处理。


      UI线程图形界面中的view可通过post方法向GUI线程的message queue投递一个runnable。
     对于除UI线程以外的其他线程,创建时缺省并没有message queue,
     而对于UI线程,则可以直接(比如在onCreate)创建一个handler并重载handleMessage,
     省去创建message queue的过程。
作者 east
Android, bug清单 1月 5,2021

Volley解决接收数据出现中文显示乱码问题

Volley接收数据后,若收到的数据header内无charset定义,则使用其默认的”ISO-8859-1″格式进行解析,导致乱码出现。

解决方法:

1. 自定义request,重写parseNetworkResponse方法:

 
private class mJsonRequest extends JsonObjectRequest {  
  
        /** 
         * <p>Description:{转码}</p> 
         * @author:yangbiyao@163.com 
         * @see com.android.volley.toolbox.JsonObjectRequest#parseNetworkResponse(com.android.volley.NetworkResponse) 
         */  
        @Override  
        protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {  
            try {  
                response.headers.put("HTTP.CONTENT_TYPE", "utf-8");  
//                String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));  
                String jsonString = new String(response.data,"utf-8");  
                return Response.success(new JSONObject(jsonString), HttpHeaderParser.parseCacheHeaders(response));  
            }  
            catch (UnsupportedEncodingException e) {  
                return Response.error(new ParseError(e));  
            }  
            catch (JSONException je) {  
                return Response.error(new ParseError(je));  
            }  
        }  
  
        /** 
         * @param method 
         * @param url 
         * @param jsonRequest 
         * @param listener 
         * @param errorListener 
         */  
        public mJsonRequest(int method, String url, JSONObject jsonRequest, Listener<JSONObject> listener,  
                ErrorListener errorListener) {  
            super(method, url, jsonRequest, listener, errorListener);  
        }  
  
        /** 
         * @param url 
         * @param jsonRequest 
         * @param listener 
         * @param errorListener 
         */  
        public mJsonRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener,  
                ErrorListener errorListener) {  
            super(url, jsonRequest, listener, errorListener);  
        }  
  
    }  

 
String jsonString = new String(response.data,"utf-8");  

在此转为我们想要的utf-8格式即可。

方法 2:

直接将数据转码为我们想要的utf-8(或GBK):

new String(data.getBytes(“ISO-8859-1″),”utf-8”)

data为乱码的数据。

作者 east
Android 1月 5,2021

android http请求带中文参数会乱码(url编码)

URL url = new URL(“http://www.my400800.cn &search=400电话 “);   HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();

时传递到服务端的中文字符时乱码,最后是将要发送的字符经过编号就可以了代码修改如下:

 URL url = new URL(“http://www.my400800.cn &search=”+java.net.URLEncoder.encode(“400电话 “));   HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();

作者 east
Android 12月 16,2020

Android聊天输入框控件

public class ChatInputWidget extends LinearLayout implements OnClickListener {
    private final String TAG = getClass().getSimpleName();
    private ViewPager mEmojiPager;
    private GifEditText mEditText;
    private ImageView mEmojiImageView;
    private Button mSendButton;
    private View pagerView;
    private CircleIndicator circleIndicator;

    private Handler handler = new Handler();
    private List<Emoji> mEmojiList;

    private static final String FILE_NAME = "emoji.txt";
    private static final String GIFT_RES_FILE_PATH = "emoji";
    private static final String ASSETS_FILE_NAME = GIFT_RES_FILE_PATH + "/" + FILE_NAME;
    private final int pagerNum = 10;

    protected Context mContext;
//    protected InputMethodManager inputManager;

    private ChatInputMenuListener mListener;
    private int mToUserId;
    private int mToVipLevel;
    private String toUserName;

    public String getToUserName() {
        return toUserName;
    }

    public void setToUserName(String toUserName) {
        this.toUserName = toUserName;
    }

    public void setToVipLevel(int toVipLevel) {
        this.mToVipLevel = toVipLevel;
    }

    public ChatInputWidget(Context context) {
        super(context);
        if (context instanceof Activity) {
            ((Activity) context).getLayoutInflater().inflate(R.layout.widget_chat_input, this);
        } else {
            inflate(context, R.layout.widget_chat_input, this);
        }
        initView(context);
    }

    public ChatInputWidget(Context context, AttributeSet attrs) {
        super(context, attrs);
        if (context instanceof Activity) {
            ((Activity) context).getLayoutInflater().inflate(R.layout.widget_chat_input, this);
        } else {
            inflate(context, R.layout.widget_chat_input, this);
        }
        initView(context);
    }

    private void initView(Context context) {
        this.mContext = context;
//        inputManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);

        mEditText = (GifEditText) findViewById(R.id.chat_et_message);
        mEmojiPager = (ViewPager) findViewById(R.id.chat_vp_face_pager);
        mEmojiImageView = (ImageView) findViewById(R.id.chat_iv_face);
        mSendButton = (Button) findViewById(R.id.chat_bt_send);
        pagerView = findViewById(R.id.rl_chat_pager);
        circleIndicator = (CircleIndicator) findViewById(R.id.indicator_chat_emoji);

    }

    public void init() {
        mSendButton.setOnClickListener(this);
        mEmojiImageView.setOnClickListener(this);
        mEditText.setOnClickListener(this);
        mEditText.setOnFocusChangeListener(new OnFocusChangeListener() {

            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    hideEmojicon();
                }
            }
        });

        mEmojiList = getExpressionRes();
        List<View> views = new ArrayList<>();

        int pagerSize;

        if (mEmojiList.size() % pagerNum == 0) {
            pagerSize = mEmojiList.size() / pagerNum;
        } else {
            pagerSize = mEmojiList.size() / pagerNum + 1;
        }

//        LogUtil.w(TAG, "pagerSize === " + pagerSize);

        for (int i = 0; i < pagerSize; i++) {
            if (i == pagerSize - 1) {
                views.add(getGridChildView(mEmojiList.subList(i * pagerNum, mEmojiList.size())));
            } else {
                views.add(getGridChildView(mEmojiList.subList(i * pagerNum, (i + 1) * pagerNum)));
            }
        }
        mEmojiPager.setAdapter(new EmojiPagerAdapter(views));
        circleIndicator.setViewPager(mEmojiPager);
    }

    /**
     * 显示或隐藏表情页
     */
    private void toggleEmojicon() {
        if (pagerView.getVisibility() == View.GONE) {
            hideKeyboard();
            handler.postDelayed(new Runnable() {
                public void run() {
                    showEmojicon();
                }
            }, 100);
        } else {
            hideEmojicon();
            handler.postDelayed(new Runnable() {
                public void run() {
                    showKeyboard();
                }
            }, 100);
        }
    }

    /**
     * 隐藏表情页
     */
    public void hideEmojicon() {
        pagerView.setVisibility(View.GONE);
        mEmojiImageView.setSelected(false);
    }

    private void showEmojicon() {
        pagerView.setVisibility(View.VISIBLE);
        mEmojiImageView.setSelected(true);
    }

    /**
     * 隐藏表情按钮
     */
    public void hideEmojiButton() {
        mEmojiImageView.setVisibility(View.GONE);
    }

    public void setHint(String hint) {
        mEditText.setHint(hint);
    }

    public void setHint(int resid) {
        mEditText.setHint(resid);
    }

    public String getText() {
        return mEditText.getText().toString();
    }

    public void setText(String text) {
        mEditText.setText(text);
        if (text != null) {    //光标移到最后
            mEditText.setSelection(text.length());
        }
    }

    public void setText(int resid) {
        mEditText.setText(resid);
    }

    public void setFocus() {
        mEditText.setFocusable(true);
        mEditText.setFocusableInTouchMode(true);
        mEditText.requestFocus();
        mEditText.requestFocusFromTouch();
        mEditText.postDelayed(new Runnable() {
            @Override
            public void run() {
                AppUtil.showInputMethod(mEditText);
            }
        }, 50);
    }

    public View getEditText() {
        return mEditText;
    }

    /**
     * 隐藏软键盘
     */
    public void hideKeyboard() {
        AppUtil.hideInputMethod(mEditText);
    }

    public void showKeyboard() {
        AppUtil.showInputMethod(mEditText);
    }

    /**
     * 系统返回键被按时调用此方法
     *
     * @return 返回false表示返回键时扩展菜单栏时打开状态,true则表示按返回键时扩展栏是关闭状态<br/>
     * 如果返回时打开状态状态,会先关闭扩展栏再返回值
     */
    public boolean onBackPressed() {
        if (pagerView.getVisibility() == View.VISIBLE) {    //隐藏表情
            hideEmojicon();
            return false;
        } else {
            return true;
        }

    }

    private void sendMessage() {
        //通过回调通知发送消息
        if (mListener != null) {
            String message = StrUtil.escapeForXML(mEditText.getText().toString());
            if (mListener.onSendMessage(message, mToUserId, mToVipLevel, toUserName)) {
                //清空内容
                mEditText.setText(null);
            }
        }
    }

    public void setToUserId(int userId) {
        mToUserId = userId;
    }

    public int getToUserId() {
        return mToUserId;
    }

    /**
     * 清除输入框内容
     */
    public void clearText() {
        mEditText.setText(null);
    }

    public void setChatInputMenuListener(ChatInputMenuListener listener) {
        this.mListener = listener;
    }

    private void insertEmoji(String name) {
        final int pos = mEditText.getSelectionStart();
        final String faceString = "[$" + name + "$]";
        final Editable text = (Editable) mEditText.getText();
        text.insert(pos, faceString);
        final GifDrawable drawable = EmojiHelper.getInstance().getGifDrawable(name);
        if (drawable != null) {
            final ImageSpan gifSpan = new GifImageSpan(drawable);
            text.setSpan(gifSpan, pos, pos + faceString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
    }

    private List<Emoji> getExpressionRes() {
        List<Emoji> reslist = new ArrayList<>();
        String emojiJson = FileUtil.readAssetsByName(mContext, ASSETS_FILE_NAME);

        try {
            reslist = parseGiftList(new JSONObject(emojiJson));
        } catch (Exception e) {
            LogUtil.e(TAG, "read local gift error:" + e.toString());
        }
        return reslist;
    }

    private List<Emoji> parseGiftList(JSONObject json) throws Exception {
        List<Emoji> giftList = new ArrayList<>();
        JSONArray jsonArray = json.getJSONArray("emoji");
        if (jsonArray != null) {
            for (int i = 0; i < jsonArray.length(); i++) {
                giftList.add(parseGift(jsonArray.getJSONObject(i)));
            }
        }
        return giftList;
    }

    private Emoji parseGift(JSONObject json) throws Exception {
        Emoji emoji = new Emoji();
        emoji.setId(json.getString("id"));
        emoji.setName(json.getString("name"));
        return emoji;
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.chat_iv_face:
                toggleEmojicon();
                break;
            case R.id.chat_et_message:
                hideEmojicon();
                break;
            case R.id.chat_bt_send:
                sendMessage();
                break;
        }
    }

    /**
     * 获取表情的gridview的子view
     *
     * @param
     * @return
     */
    private View getGridChildView(List<Emoji> list) {
        final EmojiGridAdapter expressionAdapter = new EmojiGridAdapter(mContext, list);
        GridView gridView = (GridView) LayoutInflater.from(mContext).inflate(R.layout.layout_emoji_grid, null);
        gridView.setAdapter(expressionAdapter);
        gridView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                try {
                    // 文字输入框可见时,才可输入表情
                    if (mEditText.getVisibility() == View.VISIBLE) {
                        insertEmoji(expressionAdapter.getItem(position).getId());
                    }
                } catch (Exception e) {
                }

            }
        });
        return gridView;
    }

    public class EmojiPagerAdapter extends PagerAdapter {

        private List<View> views;

        public EmojiPagerAdapter(List<View> views) {
            this.views = views;
        }

        @Override
        public int getCount() {
            return views.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ((ViewPager) container).addView(views.get(position));
            return views.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            ((ViewPager) container).removeView(views.get(position));

        }

    }

    private class EmojiGridAdapter extends QuickAdapter<Emoji> {

        public EmojiGridAdapter(Context context, List<Emoji> objects) {
            super(context, R.layout.item_emoji, objects);
        }

        @Override
        protected void convert(BaseAdapterHelper helper, Emoji item) {
            helper.setText(R.id.emoji_tv_expression, item.getName());
            helper.setImageDrawable(R.id.emoji_iv_expression, EmojiHelper.getInstance().getPngDrawable(item.getId()));
        }

    }

    public int getChatInputLength() {
        if (null != mEditText) {
            return mEditText.getChatInputLength();
        } else {
            return 0;
        }
    }

    public interface ChatInputMenuListener {
        /**
         * 发送消息按钮点击
         *
         * @param content  文本内容
         * @param toUserId
         */
        boolean onSendMessage(String content, int toUserId, int toVipLevel, String toUserName);

    }

}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              xmlns:skin="http://schemas.android.com/android/skin"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:background="@drawable/skin_label_gray_bg"
              android:orientation="vertical"
              skin:enable="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:gravity="center_vertical|right"
        android:orientation="horizontal">

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1.00"
            android:background="@drawable/skin_label_white_bg"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:paddingBottom="8dp"
            android:paddingRight="5dp"
            android:paddingTop="8dp"
            skin:enable="true">

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1.00"
                android:gravity="center_vertical|right"
                android:orientation="horizontal">

                <!-- <LinearLayout
                    android:id="@+id/chat_ll_to"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal" >

                    <TextView
                        android:id="@+id/chat_tv_to"
                        android:layout_width="60dp"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_vertical"
                        android:layout_marginLeft="5dp"
                        android:drawablePadding="5dp"
                        android:drawableRight="@drawable/small_up_arrow"
                        android:ellipsize="end"
                        android:gravity="right"
                        android:singleLine="true"
                        android:text="@string/chat_text_toall"
                        android:textColor="@color/common_blue"
                        android:textSize="14sp" />

                    <View
                        android:layout_width="1px"
                        android:layout_height="match_parent"
                        android:layout_marginLeft="5dp"
                        android:background="@color/line_view_item" />
                </LinearLayout> -->

                <com.gift.view.GifEditText
                    android:id="@+id/chat_et_message"
                    style="?android:attr/editTextStyle"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="5dp"
                    android:background="@null"
                    android:focusable="true"
                    android:focusableInTouchMode="true"
                    android:gravity="center_vertical"
                    android:hint="@string/chat_hint_message"
                    android:imeOptions="actionNone"
                    android:inputType="textMultiLine"
                    android:maxHeight="100dp"
                    android:maxLength="200"
                    android:textColor="@color/text_content"
                    android:textColorHint="@color/text_prompt_input"
                    android:textSize="14sp"/>
            </LinearLayout>

            <ImageView
                android:id="@+id/chat_iv_face"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="5dp"
                android:src="@drawable/button_selector_chat_face"/>
        </LinearLayout>

        <Button
            android:id="@+id/chat_bt_send"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginLeft="5dp"
            android:background="@drawable/skin_button_bg_white"
            android:paddingLeft="15dp"
            android:paddingRight="15dp"
            android:text="@string/chat_text_send"
            android:textColor="@color/button_text_color"
            android:textSize="14sp"
            skin:enable="true"/>
    </LinearLayout>

    <RelativeLayout
        android:id="@+id/rl_chat_pager"
        android:layout_width="match_parent"
        android:layout_height="270dp"
        android:background="@color/white"
        android:paddingBottom="27dp"
        android:visibility="gone"
        >

        <android.support.v4.view.ViewPager
            android:id="@+id/chat_vp_face_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:persistentDrawingCache="animation"
            />

        <com.gift.view.infiniteview.CircleIndicator
            android:id="@+id/indicator_chat_emoji"
            android:layout_width="match_parent"
            android:layout_height="6dp"
            android:layout_alignParentBottom="true"
            app:ci_background="@color/banner_indicator_selected"
            app:ci_gravity="center"
            app:ci_margin="5dp"
            app:ci_mode="solo"
            app:ci_radius="3dp"
            app:ci_selected_background="@color/text_orange"
            />

    </RelativeLayout>

</LinearLayout>
作者 east
Android 12月 16,2020

Android直播间刷礼物动画控件

public class GiftWidget extends LinearLayout implements OnClickListener, GiftNumberChangeListener {
    private ViewPager mViewPager;
    private Button mGiveButton;
    private View mNumberView;
    private TextView mBalanceTextView;
    private TextView mNumberTextView;
    private LinearLayout mIndicatorView;
    private TextView pay;

    private final static int GIFT_MAX_ITEM_SIZE = 10;
    private int indicatorSpace = 5;
    private int mNumber;
    private String mSelectedGiftId;

    private GiftNumberPopup mGiftNumberPopup;
    private List<GiftGridAdapter> mAdapterList = new ArrayList<GiftGridAdapter>();

    private GiftGiveClickListener mGiftGiveListener;
    private Context mContext;

    public GiftWidget(Context context) {
        super(context);
        if (context instanceof Activity) {
            ((Activity) context).getLayoutInflater()
                    .inflate(R.layout.widget_gift, this);
        } else {
            inflate(context, R.layout.widget_gift, this);
        }
        initView(context);
    }

    public GiftWidget(Context context, AttributeSet attrs) {
        super(context, attrs);
        if (context instanceof Activity) {
            ((Activity) context).getLayoutInflater()
                    .inflate(R.layout.widget_gift, this);
        } else {
            inflate(context, R.layout.widget_gift, this);
        }
        initView(context);
    }

    private void initView(Context context) {
        this.mContext = context;
        mViewPager = (ViewPager) findViewById(R.id.gift_vp_pager);
        mGiveButton = (Button) findViewById(R.id.gift_bt_give);
        mNumberView = findViewById(R.id.gift_ll_number);
        mBalanceTextView = (TextView) findViewById(R.id.gift_tv_balance);
        mNumberTextView = (TextView) findViewById(R.id.gift_tv_number);
        mIndicatorView = (LinearLayout) findViewById(R.id.gift_ll_indicator);
        pay = (TextView) findViewById(R.id.gift_tv_balance_cz);
        mNumberView.setOnClickListener(this);
        mGiveButton.setOnClickListener(this);
        pay.setOnClickListener(this);
    }

    public void initGift(List<Gift> giftList) {
        if (giftList == null) {
            return;
        }
        mAdapterList.clear();
        if (giftList.size() > 0 && mSelectedGiftId == null) {    //默认选中第一个礼物
            mSelectedGiftId = giftList.get(0).getId();
        }
        final List<View> views = new ArrayList<View>();
        int start = 0;
        while (start < giftList.size()) {
            int count;
            if (giftList.size() - start > GIFT_MAX_ITEM_SIZE) {
                count = GIFT_MAX_ITEM_SIZE;
            } else {
                count = giftList.size() - start;
            }
            List<Gift> list = giftList.subList(start, start + count);
            start = start + count;
            views.add(getGridChildView(list));
        }
        initIndicator(views.size());

        mViewPager.setAdapter(new GiftPagerAdapter(views));
        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                switchIndicator(position);
                //刷新旧的选中状态
                if (mAdapterList.size() > position) {
                    GiftGridAdapter adapter = mAdapterList.get(position);
                    adapter.notifyDataSetChanged();
                }
            }
        });
        switchIndicator(0);
    }

    private void initIndicator(int count) {
        mIndicatorView.removeAllViews();
        for (int i = 0; i < count; i++) {
            ImageView indicator = new ImageView(getContext());
            indicator.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            indicator.setPadding(indicatorSpace, indicatorSpace, indicatorSpace, indicatorSpace);
            indicator.setImageResource(R.drawable.icon_indicator);
            mIndicatorView.addView(indicator);
        }
    }

    private void switchIndicator(int position) {
        for (int i = 0; i < mIndicatorView.getChildCount(); i++) {
            ((ImageView) mIndicatorView.getChildAt(i)).setSelected(i == position ? true : false);
        }
    }

    private View getGridChildView(List<Gift> giftList) {
        final GiftGridAdapter adapter = new GiftGridAdapter(getContext(), 0, giftList);
        mAdapterList.add(adapter);
        GridView gridView = (GridView) LayoutInflater.from(getContext()).inflate(R.layout.layout_gift_grid, null);
        gridView.setAdapter(adapter);
        gridView.setChoiceMode(GridView.CHOICE_MODE_SINGLE);
        gridView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                mSelectedGiftId = adapter.getItem(position).getId();
                adapter.notifyDataSetChanged();
            }
        });
        return gridView;
    }

    public class GiftPagerAdapter extends PagerAdapter {

        private List<View> views;

        public GiftPagerAdapter(List<View> views) {
            this.views = views;
        }

        @Override
        public int getCount() {
            return views.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ((ViewPager) container).addView(views.get(position));
            return views.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            ((ViewPager) container).removeView(views.get(position));

        }

    }

    private class GiftGridAdapter extends ArrayAdapter<Gift> {

        public GiftGridAdapter(Context context, int textViewResourceId, List<Gift> giftList) {
            super(context, textViewResourceId, giftList);
        }


        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
            if (convertView == null) {
                if (getContext() instanceof Activity) {
                    convertView = ((Activity) getContext()).getLayoutInflater().inflate(R.layout.item_gift, null);
                } else {
                    convertView = View.inflate(getContext(), R.layout.item_gift, null);
                }
                holder = new ViewHolder();
                holder.root = convertView.findViewById(R.id.gift_item_ll_root);
                holder.imageView = (ImageView) convertView.findViewById(R.id.gift_item_iv_gift);
                holder.nameTextView = (TextView) convertView.findViewById(R.id.gift_item_tv_name);
                holder.priceTextView = (TextView) convertView.findViewById(R.id.gift_item_tv_price);
                holder.line = convertView.findViewById(R.id.line);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }

            if (position > 4) {
                holder.line.setVisibility(View.GONE);
            } else {
                holder.line.setVisibility(View.VISIBLE);
            }
            Gift gift = getItem(position);
            try {
                GiftHelper.getInstance().setPngDrawable(holder.imageView, gift);
                holder.nameTextView.setText(gift.getName());
                holder.priceTextView.setText(gift.getPrice());
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (gift.getId().equals(mSelectedGiftId)) {    //选中
                holder.root.setSelected(true);
            } else {
                holder.root.setSelected(false);
            }

            return convertView;
        }

        private class ViewHolder {
            View root;
            View line;
            ImageView imageView;
            TextView nameTextView;
            TextView priceTextView;
        }
    }

    public void initGiftNumber(Activity activity) {
        if (mGiftNumberPopup == null) {
            mGiftNumberPopup = new GiftNumberPopup(activity);
            mGiftNumberPopup.setNumberChangeListener(this);
        }
        //默认显示数量1
        setGiftNumber(1);
    }

    private void showGiftNumber(View view) {
        mGiftNumberPopup.showPopupWindow(view);
    }

    public void dismissGiftNumber() {
        mGiftNumberPopup.dismiss();
    }

    public void setBalance(String text) {
        mBalanceTextView.setText(text);
    }

    private void setGiftNumber(int number) {
        mNumber = number;
        mNumberTextView.setText(String.valueOf(number));
    }

    public void setGiveGift(OnClickListener listener) {
        mGiveButton.setOnClickListener(listener);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.gift_bt_give:
                if (mGiftGiveListener != null && mSelectedGiftId != null) {
                    mGiftGiveListener.onGiftGiveClick(Integer.parseInt(mSelectedGiftId), mNumber);
                }
                break;
            case R.id.gift_ll_number:
                showGiftNumber(v);
                break;
            case R.id.gift_tv_balance_cz:
                RechargeActivity.startActivity(mContext);
                mGiftGiveListener.dismiss();
                break;
        }
    }

    @Override
    public void onGiftNumberChange(int number) {
        setGiftNumber(number);
    }

    public void setOnGiftGiveClickListener(GiftGiveClickListener listener) {
        mGiftGiveListener = listener;
    }

    public interface GiftGiveClickListener {
        public void onGiftGiveClick(int giftId, int number);

        public void dismiss();
    }
}
<?xml version="1.0" encoding="utf-8"?>
<!-- 礼物控件layout -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:skin="http://schemas.android.com/android/skin"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="vertical">


    <com.giflist.view.AdjustHeightViewPager
        android:id="@+id/gift_vp_pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:flipInterval="300"
        android:persistentDrawingCache="animation"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="5dp"
        android:orientation="vertical">

        <LinearLayout
            android:id="@+id/gift_ll_indicator"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="horizontal"/>

        <View
            android:layout_width="match_parent"
            android:layout_height="2px"
            android:layout_marginTop="7dp"
            android:background="@color/line_list"
            skin:enable="true"/>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp">

            <TextView
                android:id="@+id/gift_tv_balance_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:text="@string/gift_text_balance"
                android:textColor="@color/text_content"
                android:textSize="14sp"/>

            <TextView
                android:id="@+id/gift_tv_balance"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginRight="5dp"
                android:layout_toLeftOf="@+id/gift_tv_balance_cz"
                android:layout_toRightOf="@+id/gift_tv_balance_label"
                android:ellipsize="end"
                android:singleLine="true"
                android:textColor="@color/text_content"
                android:textSize="14sp"/>

            <TextView
                android:id="@+id/gift_tv_balance_cz"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginRight="@dimen/top_margin"
                android:layout_toLeftOf="@+id/gift_ll_number"
                android:text="@string/recharge_title"
                android:textColor="@color/skin_selector_text_color"
                android:textSize="14sp"
                skin:enable="true"/>


            <LinearLayout
                android:id="@+id/gift_ll_number"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_centerVertical="true"
                android:paddingBottom="10dp"
                android:paddingTop="10dp"
                android:layout_marginRight="10dp"
                android:layout_toLeftOf="@+id/gift_bt_give"
                android:background="@drawable/button_gray_selector"
                android:clickable="true"
                android:gravity="right|center_vertical"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/gift_tv_number"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/skin_selector_text_color"
                    android:textSize="14sp"
                    android:minWidth="50dp"
                    android:gravity="center"
                    android:text="1"
                    skin:enable="true"/>
                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:paddingRight="10dp"
                    android:src="@drawable/video_present_number_point_skin_n"/>
            </LinearLayout>

            <Button
                android:id="@+id/gift_bt_give"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:background="@drawable/skin_button_orange_selector"
                android:paddingBottom="10dp"
                android:paddingLeft="25dp"
                android:paddingRight="25dp"
                android:paddingTop="10dp"
                android:text="@string/gift_text_give"
                android:textColor="@color/white"
                skin:enable="true"/>
        </RelativeLayout>
    </LinearLayout>

</LinearLayout>
作者 east
Android 12月 11,2020

Android自定义交易密码框


/**
 * 交易密码弹出框
 * Created by Administrator on 2016/4/18.
 */
public class TradePasswordPopup extends BasePopupWindow {

    private Button popupSure, popcancel;
    private MyGridPasswordView passwordEdit;
    private LinearLayout tip_text_layout;
    private TextView incorrect_password;
    private OnFocus onFocus;
    private OnClickListener mListener;

    public TradePasswordPopup(Activity context) {
        super(context, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        mGravity = Gravity.CENTER;
    }

    protected boolean getFocusable() {
        return true;
    }

    @Override
    protected void initView(Activity context) {
        popupSure = (Button) mPopupView.findViewById(R.id.room_password_ok);
        popcancel = (Button) mPopupView.findViewById(R.id.room_password_cancel);
        passwordEdit = (MyGridPasswordView) mPopupView.findViewById(R.id.password_edit);
        incorrect_password = (TextView) mPopupView.findViewById(R.id.tx_tradepassword_error);
        tip_text_layout = (LinearLayout) mPopupView.findViewById(R.id.passworld_edit);
      /*  incorrect_password.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG);
        incorrect_password.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mListener != null) {
                    mListener.onClick(incorrect_password);
                }
                dismiss();
            }
        }); */
        popupSure.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                onFocus.focus(passwordEdit.getPassWord());
            }
        });
        popcancel.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
               dismiss();
            }
        });
        passwordEdit.clearPassword();
        passwordEdit.forceInputViewGetFocus();
        showInputMethod(passwordEdit.getInputView(), 100);
    }

    public void showPopupWindow() {
       super.showPopupWindow();

    }

    public void clearPassword(){
        passwordEdit.clearPassword();
    }

    public void setAskPasswordListener(OnClickListener listener) {
        mListener = listener;
    }

    public void setListener(OnFocus onFocus) {
        this.onFocus = onFocus;
    }

    public interface OnFocus {
        void focus(String txt);
    }

    @Override
    protected Animation getShowAnimation() {
        return null;
    }

    @Override
    protected View getClickToDismissView() {
        return null;
    }

    @Override
    public View getPopupView() {
        return getPopupViewById(R.layout.popup_trade_password_view);
    }

    @Override
    public View getAnimaView() {
        return mPopupView;
    }

    @Override
    public boolean getPopupTransparent() {
        return false;
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:id="@+id/passworld_edit"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="bottom"
    android:gravity="center"
    android:orientation="vertical">


    <LinearLayout

        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/popup_dialog_bg"
        android:gravity="center"
        android:layout_marginLeft="30dp"
        android:layout_marginRight="30dp"
        android:orientation="vertical"
        android:visibility="visible">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <Button
                android:id="@+id/room_password_cancel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:background="@drawable/white_gray_bottomleft_radius"
                android:paddingBottom="20dp"
                android:paddingTop="20dp"
                android:text="@string/cancel"
                android:textColor="@color/text_b2"
                android:textSize="16sp" />

            <TextView
                android:id="@+id/room_password_prompt_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:gravity="center"
                android:padding="10dp"
                android:text="@string/trade_input_trade_password"
                android:textColor="#343434"
                android:textSize="16dp" />


            <Button
                android:id="@+id/room_password_ok"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:background="@drawable/white_gray_bottomright_radius"
                android:paddingBottom="20dp"
                android:paddingTop="20dp"
                android:text="@string/ok"
                android:textColor="@color/bg_title"
                android:textSize="16sp" />
        </RelativeLayout>

        <com.bitvf.bitcoinWallet.view.MyGridPasswordView
            android:id="@+id/password_edit"
            android:layout_width="fill_parent"
            android:layout_height="50dip"
            android:layout_gravity="center_horizontal"
            android:layout_marginBottom="@dimen/verify_code_edit_margin_bottom"
            android:layout_marginLeft="@dimen/verify_code_margin"
            android:layout_marginRight="@dimen/verify_code_margin"
            android:layout_marginTop="30dp"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:gravity="center"
            app:gpvLineColor="@color/divive_line"
            app:gpvLineWidth="@dimen/verify_code_border_width"
            app:gpvPasswordLength="6"
            app:gpvPasswordTransformation="●"
            app:gpvPasswordType="textPassword"
            app:gpvTextSize="18sp" />

        <TextView
            android:id="@+id/tx_tradepassword_error"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dip"
            android:textSize="14sp"
            android:layout_marginTop="5dip"
            android:layout_marginBottom="40dip"
            android:textColor="@color/text_warn"
            tools:text="密码错误,请重新输入"
            />


    </LinearLayout>
</LinearLayout>
作者 east

上一 1 … 3 4 5 … 7 下一个

关注公众号“大模型全栈程序员”回复“小程序”获取1000个小程序打包源码。回复”chatgpt”获取免注册可用chatgpt。回复“大数据”获取多本大数据电子书

标签

AIGC AI创作 bert chatgpt github GPT-3 gpt3 GTP-3 hive mysql O2O tensorflow UI控件 不含后台 交流 共享经济 出行 图像 地图定位 外卖 多媒体 娱乐 小程序 布局 带后台完整项目 开源项目 搜索 支付 效率 教育 日历 机器学习 深度学习 物流 用户系统 电商 画图 画布(canvas) 社交 签到 联网 读书 资讯 阅读 预订

官方QQ群

小程序开发群:74052405

大数据开发群: 952493060

近期文章

  • 如何在Chrome中设置启动时自动打开多个默认网页
  • spark内存溢出怎样区分是软件还是代码原因
  • MQTT完全解析和实践
  • 解决运行Selenium报错:self.driver = webdriver.Chrome(service=service) TypeError: __init__() got an unexpected keyword argument ‘service’
  • python 3.6使用mysql-connector-python报错:SyntaxError: future feature annotations is not defined
  • 详解Python当中的pip常用命令
  • AUTOSAR如何在多个供应商交付的配置中避免ARXML不兼容?
  • C++thread pool(线程池)设计应关注哪些扩展性问题?
  • 各类MCAL(Microcontroller Abstraction Layer)如何与AUTOSAR工具链解耦?
  • 如何设计AUTOSAR中的“域控制器”以支持未来扩展?

文章归档

  • 2025年7月
  • 2025年6月
  • 2025年5月
  • 2025年4月
  • 2025年3月
  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年1月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年7月
  • 2018年6月

分类目录

  • Android (73)
  • bug清单 (79)
  • C++ (34)
  • Fuchsia (15)
  • php (4)
  • python (45)
  • sklearn (1)
  • 云计算 (20)
  • 人工智能 (61)
    • chatgpt (21)
      • 提示词 (6)
    • Keras (1)
    • Tensorflow (3)
    • 大模型 (1)
    • 智能体 (4)
    • 深度学习 (14)
  • 储能 (44)
  • 前端 (5)
  • 大数据开发 (491)
    • CDH (6)
    • datax (4)
    • doris (31)
    • Elasticsearch (15)
    • Flink (78)
    • flume (7)
    • Hadoop (19)
    • Hbase (23)
    • Hive (41)
    • Impala (2)
    • Java (71)
    • Kafka (10)
    • neo4j (5)
    • shardingsphere (6)
    • solr (5)
    • Spark (100)
    • spring (11)
    • 数据仓库 (9)
    • 数据挖掘 (7)
    • 海豚调度器 (10)
    • 运维 (34)
      • Docker (3)
  • 小游戏代码 (1)
  • 小程序代码 (139)
    • O2O (16)
    • UI控件 (5)
    • 互联网类 (23)
    • 企业类 (6)
    • 地图定位 (9)
    • 多媒体 (6)
    • 工具类 (25)
    • 电商类 (22)
    • 社交 (7)
    • 行业软件 (7)
    • 资讯读书 (11)
  • 嵌入式 (71)
    • autosar (63)
    • RTOS (1)
    • 总线 (1)
  • 开发博客 (16)
    • Harmony (9)
  • 技术架构 (6)
  • 数据库 (32)
    • mongodb (1)
    • mysql (13)
    • pgsql (2)
    • redis (1)
    • tdengine (4)
  • 未分类 (7)
  • 程序员网赚 (20)
    • 广告联盟 (3)
    • 私域流量 (5)
    • 自媒体 (5)
  • 量化投资 (4)
  • 面试 (14)

功能

  • 登录
  • 文章RSS
  • 评论RSS
  • WordPress.org

All Rights Reserved by Gitweixin.本站收集网友上传代码, 如有侵犯版权,请发邮件联系yiyuyos@gmail.com删除.