widget_widgets - CSDN
精华内容
参与话题
  • Widget组件及设计介绍

    千次阅读 2011-08-04 23:47:06
    以下仅代表个人的想法,仅供大家参考,...我们可以看到Widget是一种展示在手机主屏的一种快速浏览的一个插件。 Widget的应用很广,可以应用到WEB、桌面和手机端。例如操作系统上的时钟、天气、资讯的小插件都属于Widge
       以下仅代表个人的想法,仅供大家参考,如有不对之处请大家指出,这也是我调研的结果,加以总结:
    

    我们可以看到Widget是一种展示在手机主屏的一种快速浏览的一个插件。

    Widget的应用很广,可以应用到WEB、桌面和手机端。例如操作系统上的时钟、天气、资讯的小插件都属于Widget。

    现在的智能手机就相当于一智能电脑,其桌面也可以填充格式各样的Widget,这里Android SDK1.5以上就提供了对Widget非常好的支持。

    Widget有几大特点

    1、身材微:一般的它们都比较小,在终端上嵌入十分的方便,运行快速。

    2、形式多:Widget的展现形式多,可以自定义样式,如幻灯秀、视频、地图、新闻、小游戏等..

    3、个性化:可以在桌面上展示个性化的服务,根据自己的需求来排列、显示。也可以设置一些widget的显示形式和更新频率。

    4、连动性:对于一个主应用程序,其相当于是一个桌面窗口一样和主程序在实时连动,可以显示一些主要的信息内容,在不必要时不需要进入主程序。

    Android上主流的一些APP Widget应用:

    其中包括新浪微博、人人网、小米读书、墨迹天气、开关控件、QQ、日期等一些的应用。

    Android上是怎么实现APP Widget的?

       实际上在Android发开包中,它把所有的一些控件包括button、txtarea、menu等等都叫做Widget,而我们实际上说的Widget 是属于 APP Widget(APPLIACTION),这个只要大家了解一下就可以了。     

    Widget的实现对于Android 上并不是什么难事,在Android SDK1.5版本的时候就已经推出了Widget的功能,并且Android手机也自带了许多插件,虽然有些并不是很个性,美观,但是我们还可以选择很多第三方开发的Widget来个性化自己的桌面。

    Android上的APP  Widget设计

    首先设计尺寸规则有一个公示:最小尺寸(dip)=(格子数*74)-2

    所谓的格子数就是指.主屏分为4X4的格子根据自己设计占的格子数来创建的。

    标准Widget剖析

    典型的AndroidWidget主要有三个组成部分:一个限位框,一个框架,还有Widget的图形控件以及其它元素.设计周全的Widget会在限位框边缘&框架之间,及框架内边缘&Widget的控件之间都保留一些内填充(内补白).Widget的外观被设计得与主屏幕的其它Widget相匹配,并以主屏幕的其它元素为依据对齐;它们亦使用标准的阴影效果.此文档说明了所有的相关细节.

                     标准Widget尺寸(纵向)

                标准Widget尺寸(横向)

    设计一个Widget

    ◆为你的Widget选择限位框尺寸.

    最有效的Widget会以最小型的尺寸来显示程序有用或及时的数据.用户会衡量Widget的有用性或它所占的屏幕空间,因此越小越好.

    所有Widget必须符合限位框的六种尺寸之一,或者更好的是,或更好的是在一对纵向和横向的方位尺寸里,这样在用户切换屏幕方向时,你的Widget看起来也会更舒适.

    标准Widget尺寸以图例说明了六种Widget尺寸的限位规格(三种纵向三种横向).

    ◆选择匹配的框架.

    标准Widget框架以图例说明了六种Widget尺寸的标准框架,你可以下载此链接的副本备用.你的Widget并非都必须使用这些框架,但若你用了,你的Widget可能与其它Widget看起来更一致.

     

    ◆对图形应用标准阴影效果.

    此外,你并非必须使用此效果,但标准Widget阴影说明了标准Widget使用的Photoshop设置.

    ◆若你的Widget包含按钮,需绘制按钮的三种状态(默认,按下,被选中).

    你可以下载一个音乐Widget播放按钮的Photoshop文件(包含三种状态) ,用来分析三种标准按钮效果的Photoshop设置.

    标准Widget尺寸

    这里有六种基于4 x 4 (纵向)或 4 x 4 (横向)单元的主屏幕网格的标准Widget尺寸.这些规格为六种标准Widget尺寸的限位框.这些尺度是六种标准Widget尺寸限位框.典型Widget的内容并不绘制这些尺度的边缘线,但在限位框里填充一个框架正如设计一个Widget所说到的.

    纵向方位时, 每个单元宽80像素高100像素(下图展示了一个纵向方位的单元). 纵向方位支持的三种Widget尺寸为:

     

       横向方位时,每个单元宽106像素高74像素.横向方位支持的三种Widget尺寸为:

     

    标准Widget框架

    针对六种标准Widget尺寸这里有标准的框架.你可以在以下内容点击框架图片来下载该框架的Photoshop 文件用在你的Widget上.

    4x1_Widget_Frame_Portrait.psd

    3x3_Widget_Frame_Portrait.psd

    2x2_Widget_Frame_Portrait.psd

    4x1_Widget_Frame_Landscape.psd

    3x3_Widget_Frame_Landscape.psd

    2x2_Widget_Frame_Landscape.psd

    更多设计的细内容请参考此处

    http://hi.baidu.com/tdskee/blog/item/575bbcaf758559d87cd92abe.html

     

     

    展开全文
  • 什么是widgetwidget是什么意思?

    万次阅读 2010-04-07 09:53:00
    这个词当然就是“widget”了。虽然已经有很多人开始谈论widgets的2.0版本,但是大部分的人才刚刚开始注意到它。你可能会觉得widgets是最烦人的时尚,或者是社会互动中极具创意的潮流。不管怎样,widgets已经成为我们...

     在过去几年,一个新术语进入了营销词汇的行列。这个词当然就是“widget”了。虽然已经有很多人开始谈论widgets的2.0版本,但是大部分的人才刚刚开始注意到它。你可能会觉得widgets是最烦人的时尚,或者是社会互动中极具创意的潮流。不管怎样,widgets已经成为我们行内对话的一部分了。这段时间widgets可谓是无所不在。和发行商、销售人员或者同事的对话,或者所阅读的文章、参加的会议等,都是围绕着这个词及其相关主题进行的。而他们的讨论的重点通常是关于widget的定义—而不是什么时候、该如何使用,或者谁出色地使用了它。

     

          全世界各地举行的市场销售讨论会议上,除了widget的定义外,讨论得最多的就是 怎么的东西才能被认可为widget。取决于公司不同的规模和业务重点,还有你在公司中担当的角色,你可能会对这个“新”事物的命名有自己的看法。可以是widget(小部件),gadget(小玩具),desktop application(桌面的应用软件),downloadable application(可下载的应用程序),或者是其他你公司自创的新鲜名称。

          紧接着,我们一起来认真思考一下widgets是什么,为什么要被使用,何时该被使用,谁来使用,如何使用等等问题。听到关于widget的各种定义后,我在Dictionary.com查找了widget的意思。正如我所想,结果和讨论一样多样:

          Widget-名词—①一个小机械设备,如球形柄或者开关装置,特别是一些没有或者想不起具体名称的物品。Gadget(新奇的小玩意):在仪器板上一排的widgets。②在用户的图形界面上,为了实现一个具体的功能(例如卷轴或者按钮)而使用的系列图形符号和程序代码。③为某项任务特定的设备或者操纵装置--通常提供包含设定模式而行为一致的常用的widgets的widget库。

          或者再看看FreeWebs.com上这个简单的,更内行的定义—widgets是能使你的网站更有个性,更特别的小的分块特征。

          其中一些定义的确与我们行业的日常活动息息相关。为了能继续我们的论述,我们假定在这个文章中提及的widgets包括①大众网页的widgets ②品牌为促进参与、销售和发展及(或)广告收益特别制作的widgets③更复杂些的可下载的多功能的widgets或者桌面应用程序。最后我想说说,这些widgets的共同点在于他们都以促进某种顾客行为(购买/收益,品牌意识/参与,传承等等)为目的。 

          为什么/何时我应该关注widgets?

          过去几年,很多品牌都已经利用了widgets。随着widgets工具的使用和市场化渐趋日常,很自然就会有更多的营销商就widgets是否适用于自己公司提出疑问。对于希望在拥挤的网络上建立吸引力和忠诚顾客群的发行者,或者努力于在自己的品牌和顾客间建设更方便的沟通桥梁的零售商,或者渴望创造下一个伟大的widget的雄心壮志的开发者,实在有太多理由去关注widgets了。但是你呢?你需要关注widgets吗?如果需要的话,又是基于什么理由呢?

          行业内不断产生一些量度widget有效性(不仅仅是指有多少人使用了你的widget)和对行业预期的促进效果评估的标准。 难度在于,基于不同目的的widget实在种类繁多。一些只是简单地宣传品牌,一些是纯熟的信息发布策略的一部分,更有一些是支持新产品、服务、电影等等的工具。当然你还可以赞助或者在别人的widget上投放广告,或者建立自己的widget……但是,当我们只是用轶闻趣事作为我们决定谁应该关注widgets的依据时,就这样一个原因就足以使所有的市场营销人员迷上widget了—widgets的世界充满机遇!但是不要忘了:同时也有很多失败的投资反例,足以吓跑那些刚进入widget世界的人了。

          因而,尽管有很多围绕着widgets世界重大突破的热火朝天的报道,widget是否能成功依然没人能打包票。所以说,公司在着手创造自己的widget的时候,应该考虑以下几点:

    1. 创造widget还是不足够的。在开始你的创造之旅前要好好地做些功课,看看widget是否适用于自己。考虑网站造访者或者品牌对象的所需,以及你要创造的widget是否实现简捷,具娱乐性,令人难忘和值得一提的。如果你满怀信心,觉得自己能打出个全垒打,觉得基于你的考虑和顾客的需求下widget的确是你必要的选择,那么是时候你要考虑如何推出你的widget,使它能脱颖而出并且持续保有竞争力了。

    2. 我需要的widget建设理念已经存在了吗?在如今这个充满竞争的社会,很多的idea可能都已经存在了。但是这并不意味着你不能对这些现有的概念进行改进/改造。关键在于你应该意识到这些概念的存在并且想出改进/改造的最佳方案,从而实现你的widget和使用者间的最优互动。

    3. 设定你的目标和效用评估标准。虽说行业的widget效用评估标准已在制订中了,但是于大部分的公司来说,只有自己才最清楚知道自己所需。你可能是出版商--为直接影响利益的读者量而努力;你可能是一个品牌网站的经营者—希望你的客户下载/使用你提供的应用程序;你也可能为分销商提供你的widget/销售引擎,优化他们和顾客间的营销行为。如果你不能很好地设定,要取得成功几乎不可能。同时,这个过程也能帮助你分析你应该和谁合作,应该投资多少等问题。

          什么时候我应该开发widget?

          同事、上司还有同行的朋友问我这个问题的时候,我会先和他们回顾一下以上我说的几点--要他们先考虑清楚他们是否需要参与到关于widgets的讨论中去。这的确是由你的目标、预算和顾客基础决定的。你的顾客会对widgets感冒吗?它有助于你把你的品牌推向另一个高度吗?你有足够的预算抵御冲击和错失吗?

          在你决定现在要你的队伍着手开发下一个伟大的widget工具的时机是否已经来临之前,或许你该考虑以下由iMediaConnection.com的同事的建议。他的文章“Avoid these Facebook faux pas”写得相当有深度。其中的至理名言有:“先好好地自我反省一下。你着手加入这场(在我这篇文章中是关于widget的)社会化媒体运动,是不是仅仅因为别人认为这样做很酷?抑或是因为它符合发展更多方法的理念?其实只需要把它看作是众多销售渠道之一,好好考虑就对了--一旦它被认定为不能成就全局战略,它便有可能失败。”

          虽然是针对社会化媒体运动而写的,他的这番言论依然与widget的话题相关。我实在想不到更好的办法去论证了。因此借用一下Evan的妙论了。谢谢Evan!

           谁在使用widgets?

          在每天都有新的widgets冒出来的市场上显然还是有一些先锋角色的。他们以发展,创新和成功作为标准建设自己的widgets。以下我列举其中几个我认为做得不错的网站。

    Tripadvisor(旅行顾问--免费旅游指南网站)

         tripadvisor网站的访问者会发现,下载网站提供的“我所访问过的城市”的widget时,向导和价值定位都相当简单。但是,其功能和效应可以说是好得不得了。这个widget会提问:“你去过哪个城市了?”创造一个可以与朋友分享/互动的旅游地图,帮助他们规划行程。我认识的所有人几乎都在使用这个widget—从十几岁的堂兄弟到60岁的岳父都在使用。我要马上计划我的旅游行程了!做得不错的网站!

    ILike(我喜欢)

         如果你经常上网,可能你也听过iLike这个网站了。引用网页的描述来介绍它吧:在iLike网站,你可以为你的档案添加音乐和视频,向你的朋友推荐音乐,查看谁去看了什么音乐会。还可以免费下载你喜欢的 mp3。你或许能在音乐常识竞赛(Music Challenge)中 打败你的朋友了!


          这些受欢迎的widget有一个共同的基本要点:为客户实现想法提供了便利的手段。譬如:和朋友讨论最喜欢的音乐/视频;看看他们和朋友都分享了什么最爱;测试他们的音乐知识;还有就是分享内容、贡献曲目。这样的开始是轻松的--价值观清晰,完全和目标客户相关。难怪它能取得巨大的成功了。

    http://info.codepub.com/2008/06/info-19918.html

    http://www.iciba.com/WIDGETS/

    http://www.w3.org/TR/widgets/

     

    展开全文
  • Android列表小部件(Widget)开发详解

    万次阅读 热门讨论 2020-09-27 16:57:53
    Widget是Android应用小部件,它可以作为一个微型视图嵌套在其他应用程序中(比如可以放置在桌面),并接收周期性更新。通常来说,小部件可以通过与App交互更方便的将App的内容展示给用户,同时它也是App的一个便捷...

    好久没博客更新了,本篇文章来学习一下如何实现一个Android列表小部件,效果可以参看下图:
    在这里插入图片描述

    这个页面如果是在App内部实现,相信只要有一点Android基础的童鞋都能很轻松写出来。但是如果放到Widget中可能就不是那么简单了。因为Widget并没有运行在我们App的进程中,而是运行在系统的SystemServer进程中。你可能会惊讶,Whf!竟然不在我们App进程中!那么是不是意味着我们也不能像在App中那样操作View控件了?答案确实如此。不过不必过于担心,为了我们能在远程进程中更新界面,Google爸爸专门为我们提供了一个RemoteViews类。从名字上看,可能会觉得RemoteViews就是一个View。但事实并非如此,RemoteViews仅仅表示的是一个View结构。它可以在远程进程中展示和更新界面。今天我们要实现的列表小部件就是基于RemoteVeiw实现的。
    那么接下来我们来学习如何实现一个桌面Widget,我们先列出要实现Widget的几个核心步骤:

    • widget页面布局
    • 小部件配置信息
    • 了解AppWidgetProvider
    • RemoteViewsFactory实现列表适配
    • 点击的事件处理

    一. 实现Widget界面

    **1.widget页面布局。**首先创建一个布局文件layout_widget.xml,内容如下:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/ll_right"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_widget"
        android:orientation="vertical">
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:background="#ccc">
    
            <ImageView
                android:id="@+id/iv_icon"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_centerVertical="true"
                android:layout_marginEnd="5dp"
                android:layout_marginStart="5dp"
                android:background="@mipmap/ic_launcher_round" />
    
            <TextView
                android:id="@+id/tv_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_toEndOf="@id/iv_icon"
                android:text="Widget" />
    
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_alignParentEnd="true"
                android:gravity="center_vertical"
                android:orientation="horizontal">
    
                <ProgressBar
                    android:id="@+id/progress_bar"
                    android:layout_width="20dp"
                    android:layout_height="20dp"
                    android:indeterminateTint="@color/colorAccent"
                    android:indeterminateTintMode="src_atop"
                    android:visibility="gone" />
    
                <TextView
                    android:id="@+id/tv_refresh"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="15dp"
                    android:text="刷新"
                    android:padding="5dp"
                    android:textSize="12sp" />
            </LinearLayout>
    
        </RelativeLayout>
    
        <ListView
            android:id="@+id/lv_device"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:columnWidth="80dip"
            android:gravity="center"
            android:horizontalSpacing="4dip"
            android:numColumns="auto_fit"
            android:verticalSpacing="4dip" />
    
    </LinearLayout>
    

    看到布局中的ListView控件,你可能会不屑一笑,都什么年代了还在用ListView?RecyclerView才是王道吧?可是我只能说句抱歉,Widget不支持RecyclerView。对,你没看错,真的不支持。在Widget中我们没办法做到想用什么就用什么,甚至觉得原生用着不爽,自己撸一个控件出来。对不起,Widget都不支持。因此Widget也有很大的局限性。我们来看下支持在Widget中运行的有哪些控件:

    A RemoteViews object (and, consequently, an App Widget) can support the following layout classes:
    FrameLayout
    LinearLayout
    RelativeLayout
    GridLayout
    And the following widget classes:
    AnalogClock
    Button
    Chronometer
    ImageButton
    ImageView
    ProgressBar
    TextView
    ViewFlipper
    ListView
    GridView
    StackView
    AdapterViewFlipper
    Descendants of these classes are not supported.

    除了上述列出的几个View,其它的包括Android原生View和自定义View是都不支持在Widget中运行的。因此基于Widget页面限制我们基本就可以告别炫酷的动画效果了。

    二.小部件配置信息

    配置信息主要是设定小部件的一些属性,比如宽高、缩放模式、更新时间间隔等。我们需要在res/xml目录下新建widget_provider.xml文件,文件名字可以任意取。文件内容如下(可做参考):

    <?xml version="1.0" encoding="utf-8"?>
    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:minHeight="180dp"
        android:minWidth="300dp"
        android:previewImage="@drawable/ic_launcher_background"
        android:initialLayout="@layout/layout_widget"
        android:updatePeriodMillis="50000"
        android:resizeMode="horizontal|vertical"
        android:widgetCategory="home_screen"> 
        
    </appwidget-provider>
    

    针对上述文件中的配置信息来做下介绍。

    • minHeight、minWidth 定义Widget的最小高度和最小宽度(Widget可以通过拉伸来调整尺寸大小)。
    • previewImage 定义添加小部件时显示的图标。
    • initialLayout 定义了小部件使用的布局。
    • ***updatePeriodMillis***定义小部件自动更新的周期,单位为毫秒。
    • resizeMode 指定了 widget 的调整尺寸的规则。可取的值有: “horizontal”, “vertical”, “none”。“horizontal"意味着widget可以水平拉伸,“vertical”意味着widget可以竖值拉伸,“none”意味着widget不能拉伸;默认值是"none”。
    • widgetCategory 指定了 widget 能显示的地方:能否显示在 home Screen 或 lock screen 或 两者都可以。它的取值包括:“home_screen” 和 “keyguard”。Android 4.2 引入。
      最后,需要我们在AndroidManifest中注册AppWidgetProvider时引用该文件,使用如下:
    <receiver android:name=".widget.ListWidgetProvider">
         ...
        <meta-data
            android:name="android.appwidget.provider"
            android:resource="@xml/widget_provider" />
    </receiver>
    

    三.了解AppWidgetProvider类

    我们来简单了解下AppWidgetProvider这个类。Widget的功能均是通过AppWidgetProvider来实现的。我们跟进源码可以发现它是继承自BroadcastReceiver类,也就是一个广播接收者。上面我们提到过RemoteViews是运行在SystemServer进程中的,再结合此处我们应该可以推测小部件的事件应该是通过广播来实现的。像小部件的添加、删除、更新、启用、禁用等均是在AppWidgetProvider中通过接受广播来完成的。看AppWidgetProvider中的几个方法:

    • onUpdate() 当Widget被添加或者被更新时会调用该方法。上边我们提到通过配置updatePeriodMillis可以定期更新Widget。但是当我们在widget的配置文件中声明了android:configure的时候,添加Widget时则不会调用onUpdate方法。
    • onEnable() 这个方法会在用户首次添加Widget时调用。
    • onAppWidgetOptionsChanged() 这个方法会在添加Widget或者改变Widget的大小时候被调用。在这个方法中我们还可以根据Widget的大小来选择性的显示或隐藏某些控件。
    • onDeleted(Context, int[]) 当控件被删除的时候调用该方法
    • onEnabled(Context) 当第一个Widget被添加的时候调用。如果用户添加了两个这个小部件,那么只有第一个添加时才会调用onEnabled.
    • onDisabled(Context) 当最后一个Widget实例被移除的时候调用这个方法。在这个方法中我们可以做一些清除工作,例如删掉临时的数据库等。
    • onReceive(Context, Intent) 当接收到广播的时候会被调用。

    上述方法中,我们需要着重关心一下onUpdate()方法和onReceive()方法。因为onUpdate()方法会在Widget被添加时候调用,我们可以在此时为Widget添加一View的些交互事件,例如点击事件。由于本篇我们要实现的是一个列表小部件。因此我们还需要RemoteViewsFactory这个类来适配列表数据。

    先来看下ListWidgetProvider这个类中的代码:

    public class ListWidgetProvider extends AppWidgetProvider {
    
        private static final String TAG = "WIDGET";
    
        public static final String REFRESH_WIDGET = "com.oitsme.REFRESH_WIDGET";
        public static final String COLLECTION_VIEW_ACTION = "com.oitsme.COLLECTION_VIEW_ACTION";
        public static final String COLLECTION_VIEW_EXTRA = "com.oitsme.COLLECTION_VIEW_EXTRA";
        private static Handler mHandler=new Handler();
        private Runnable runnable=new Runnable() {
            @Override
            public void run() {
                hideLoading(Utils.getContext());
                Toast.makeText(Utils.getContext(), "刷新成功", Toast.LENGTH_SHORT).show();
            }
        };
    
        @Override
        public void onUpdate(Context context, AppWidgetManager appWidgetManager,
                             int[] appWidgetIds) {
    
            Log.d(TAG, "ListWidgetProvider onUpdate");
            for (int appWidgetId : appWidgetIds) {
                // 获取AppWidget对应的视图
                RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);
    
                // 设置响应 “按钮(bt_refresh)” 的intent
                Intent btIntent = new Intent().setAction(REFRESH_WIDGET);
                PendingIntent btPendingIntent = PendingIntent.getBroadcast(context, 0, btIntent, PendingIntent.FLAG_UPDATE_CURRENT);
                remoteViews.setOnClickPendingIntent(R.id.tv_refresh, btPendingIntent);
    
                // 设置 “ListView” 的adapter。
                // (01) intent: 对应启动 ListWidgetService(RemoteViewsService) 的intent
                // (02) setRemoteAdapter: 设置 gridview的适配器
                //    通过setRemoteAdapter将ListView和ListWidgetService关联起来,
                //    以达到通过 ListWidgetService 更新 ListView的目的
                Intent serviceIntent = new Intent(context, ListWidgetService.class);
                remoteViews.setRemoteAdapter(R.id.lv_device, serviceIntent);
    
    
                // 设置响应 “ListView” 的intent模板
                // 说明:“集合控件(如GridView、ListView、StackView等)”中包含很多子元素,如GridView包含很多格子。
                //     它们不能像普通的按钮一样通过 setOnClickPendingIntent 设置点击事件,必须先通过两步。
                //        (01) 通过 setPendingIntentTemplate 设置 “intent模板”,这是比不可少的!
                //        (02) 然后在处理该“集合控件”的RemoteViewsFactory类的getViewAt()接口中 通过 setOnClickFillInIntent 设置“集合控件的某一项的数据”
                Intent gridIntent = new Intent();
    
                gridIntent.setAction(COLLECTION_VIEW_ACTION);
                gridIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
                PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, gridIntent, PendingIntent.FLAG_UPDATE_CURRENT);
                // 设置intent模板
                remoteViews.setPendingIntentTemplate(R.id.lv_device, pendingIntent);
                // 调用集合管理器对集合进行更新
                appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
            }
            super.onUpdate(context, appWidgetManager, appWidgetIds);
        }
    
    
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
            if (action.equals(COLLECTION_VIEW_ACTION)) {
                // 接受“ListView”的点击事件的广播
                int type = intent.getIntExtra("Type", 0);
                int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                        AppWidgetManager.INVALID_APPWIDGET_ID);
                int index = intent.getIntExtra(COLLECTION_VIEW_EXTRA, 0);
                switch (type) {
                    case 0:
                        Toast.makeText(context, "item" + index, Toast.LENGTH_SHORT).show();
                        break;
                    case 1:
                        Toast.makeText(context, "lock"+index, Toast.LENGTH_SHORT).show();
                        break;
                    case 2:
                        Toast.makeText(context, "unlock"+index, Toast.LENGTH_SHORT).show();
                        break;
                }
            } else if (action.equals(REFRESH_WIDGET)) {
                // 接受“bt_refresh”的点击事件的广播
                Toast.makeText(context, "刷新...", Toast.LENGTH_SHORT).show();
                final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
                final ComponentName cn = new ComponentName(context,ListWidgetProvider.class);
                ListRemoteViewsFactory.refresh();
                mgr.notifyAppWidgetViewDataChanged(mgr.getAppWidgetIds(cn),R.id.lv_device);
                mHandler.postDelayed(runnable,2000);
                showLoading(context);
            }
            super.onReceive(context, intent);
        }
    
        /**
         * 显示加载loading
         *
         */
        private void showLoading(Context context) {
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);
            remoteViews.setViewVisibility(R.id.tv_refresh, View.VISIBLE);
            remoteViews.setViewVisibility(R.id.progress_bar, View.VISIBLE);
            remoteViews.setTextViewText(R.id.tv_refresh, "正在刷新...");
            refreshWidget(context, remoteViews, false);
        }
    
        /**
         * 隐藏加载loading
         */
        private void hideLoading(Context context) {
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);
            remoteViews.setViewVisibility(R.id.progress_bar, View.GONE);
            remoteViews.setTextViewText(R.id.tv_refresh, "刷新");
            refreshWidget(context, remoteViews, false);
        }
    
    
    
        /**
         * 刷新Widget
         */
        private void refreshWidget(Context context, RemoteViews remoteViews, boolean refreshList) {
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
            ComponentName componentName = new ComponentName(context, ListWidgetProvider.class);
            appWidgetManager.updateAppWidget(componentName, remoteViews);
            if (refreshList)
                appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetManager.getAppWidgetIds(componentName), R.id.lv_device);
        }
    }
    

    针对以上代码,我们着重来看onUpdate()方法。在onUpdate()中我们主要实现了两个功能,第一个功能ListView以外的事件点击,例如点击“刷新”来更新小部件。第二个功能是适配ListView并实现ListView内部Item控件的点击事件。在这个方法中我们首先获取到了一个RemoteView的实例,这个RemoteView对应的就是我们Widget布局的View。关于点击事件的实现代码中注释写的也比较详细,在这里就不做过多解释了。重点是需要了解如何实现并适配ListView,具体实现请看下节。

    四.RemoteViewsFactory实现列表适配

    上面我们提到了RemoteViewsFactory,这个类其实可以类比为ListView的Adapter,该类存在的意义就是为了适配ListView的数据。只不过这里是把Adapter换成RemoteViews来实现的。看下ListRemoteViewsFactory中的代码:

    class ListRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
        private final static String TAG="Widget";
        private Context mContext;
        private int mAppWidgetId;
    
        private static List<Device> mDevices;
    
        /**
         * 构造GridRemoteViewsFactory
         */
        public ListRemoteViewsFactory(Context context, Intent intent) {
            mContext = context;
            mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID);
        }
    
        @Override
        public RemoteViews getViewAt(int position) {
            //  HashMap<String, Object> map;
    
            // 获取 item_widget_device.xml 对应的RemoteViews
            RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.item_widget_device);
    
            // 设置 第position位的“视图”的数据
            Device device = mDevices.get(position);
            //  rv.setImageViewResource(R.id.iv_lock, ((Integer) map.get(IMAGE_ITEM)).intValue());
            rv.setTextViewText(R.id.tv_name, device.getName());
    
            // 设置 第position位的“视图”对应的响应事件
            Intent fillInIntent = new Intent();
            fillInIntent.putExtra("Type", 0);
            fillInIntent.putExtra(ListWidgetProvider.COLLECTION_VIEW_EXTRA, position);
            rv.setOnClickFillInIntent(R.id.rl_widget_device, fillInIntent);
    
    
            Intent lockIntent = new Intent();
            lockIntent.putExtra(ListWidgetProvider.COLLECTION_VIEW_EXTRA, position);
            lockIntent.putExtra("Type", 1);
            rv.setOnClickFillInIntent(R.id.iv_lock, lockIntent);
    
            Intent unlockIntent = new Intent();
            unlockIntent.putExtra("Type", 2);
            unlockIntent.putExtra(ListWidgetProvider.COLLECTION_VIEW_EXTRA, position);
            rv.setOnClickFillInIntent(R.id.iv_unlock, unlockIntent);
    
            return rv;
        }
    
    
        /**
         * 初始化ListView的数据
         */
        private void initListViewData() {
            mDevices = new ArrayList<>();
            mDevices.add(new Device("Hello", 0));
            mDevices.add(new Device("Oitsme", 1));
            mDevices.add(new Device("Hi", 0));
            mDevices.add(new Device("Hey", 1));
        }
        private static int i;
        public static void refresh(){
            i++;
            mDevices.add(new Device("Refresh"+i, 1));
        }
    
        @Override
        public void onCreate() {
            Log.e(TAG,"onCreate");
            // 初始化“集合视图”中的数据
            initListViewData();
        }
    
        @Override
        public int getCount() {
            // 返回“集合视图”中的数据的总数
            return mDevices.size();
        }
    
        @Override
        public long getItemId(int position) {
            // 返回当前项在“集合视图”中的位置
            return position;
        }
    
        @Override
        public RemoteViews getLoadingView() {
            return null;
        }
    
        @Override
        public int getViewTypeCount() {
            // 只有一类 ListView
            return 1;
        }
    
        @Override
        public boolean hasStableIds() {
            return true;
        }
    
        @Override
        public void onDataSetChanged() {
        }
    
        @Override
        public void onDestroy() {
            mDevices.clear();
        }
    }
    

    有了RemoteViewsFactory 还需要有RemoteViewsService才能与ListView关联起来。来看RemoteViewsService的实现类ListWidgetService,很简单,只重写了onGetViewFactory方法:

    public class ListWidgetService extends RemoteViewsService {
    
        @Override
        public RemoteViewsService.RemoteViewsFactory onGetViewFactory(Intent intent) {
            return new ListRemoteViewsFactory(this, intent);
        }
    }
    

    至此我们可以再次回到ListWidgetProvider中的onUpdate()方法,来看ListWidgetService 是如何与ListView关联到一起的了。

     //  设置 “ListView” 的adapter。
     // (01) intent: 对应启动 ListWidgetService(RemoteViewsService) 的intent
     // (02) setRemoteAdapter: 设置 ListView的适配器
     //  通过setRemoteAdapter将ListView和ListWidgetService关联起来,
     //  以达到通过 ListWidgetService 更新 ListView 的目的
      Intent serviceIntent = new Intent(context, ListWidgetService.class);
      remoteViews.setRemoteAdapter(R.id.lv_device, serviceIntent);
    

    五.点击事件处理

    Widget中事件点击以及适配ListView,想必大家都有所了解了。那么对于事件的处理我们还没有提到,例如在Widget中点击了刷新后我们不能像在App中那样给控件设置一个事件监听来在回掉方法中处理。在文章开头我们就提到了Widget是依赖广播来实现,因此我们点击了刷新后其实仅仅是发送出来一个广播。如果我们不去处理广播那么点击事件其实是没有任何意义的。因此,来看ListWidgetProvider中第二个比较重要的方法onReceive()。这个方法比较简单,只要我们对特定的广播来做相应的处理就可以了。

    @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    	        if (action.equals(COLLECTION_VIEW_ACTION)) {//处理列表中的事件
                // 接受“ListView”的点击事件的广播
                int type = intent.getIntExtra("Type", 0);
                int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                        AppWidgetManager.INVALID_APPWIDGET_ID);
                int index = intent.getIntExtra(COLLECTION_VIEW_EXTRA, 0);
                switch (type) {
                    case 0:
                        Toast.makeText(context, "item" + index, Toast.LENGTH_SHORT).show();
                        break;
                    case 1:
                        Toast.makeText(context, "lock"+index, Toast.LENGTH_SHORT).show();
                        break;
                    case 2:
                        Toast.makeText(context, "unlock"+index, Toast.LENGTH_SHORT).show();
                        break;
                }
            } else if (action.equals(REFRESH_WIDGET)) {//处理刷新事件
                // 接受“bt_refresh”的点击事件的广播
                Toast.makeText(context, "刷新...", Toast.LENGTH_SHORT).show();
                final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
                final ComponentName cn = new ComponentName(context,ListWidgetProvider.class);
                ListRemoteViewsFactory.refresh();
                mgr.notifyAppWidgetViewDataChanged(mgr.getAppWidgetIds(cn),R.id.lv_device);
                mHandler.postDelayed(runnable,2000);
                showLoading(context);
            }
            super.onReceive(context, intent);
        }
    

    最后,别忘了ListWidgetProvider是广播,ListWidgetService是服务,都需要我们在AndroidManifest文件中来注册:

    	   <receiver android:name=".widget.ListWidgetProvider">
                <intent-filter>
                    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    
                    <!-- ListWidgetProvider接收点击ListView的响应事件 -->
                    <action android:name="com.oitsme.COLLECTION_VIEW_ACTION" />
                    <!-- ListWidgetProvider接收点击bt_refresh的响应事件 -->
                    <action android:name="com.oitsme.REFRESH_WIDGET" />
                    <action android:name="com.oitsme.LOCK_ACTION"/>
                    <action android:name="com.oitsme.UNLOCK_ACTION"/>
                </intent-filter>
                <meta-data android:name="android.appwidget.provider"
                    android:resource="@xml/widget_provider"/>
            </receiver>
    
            <service
                android:name=".widget.ListWidgetService"
                android:permission="android.permission.BIND_REMOTEVIEWS" />
    

    六.小结

    至此关于列表小部件的讲解就完成了。只是自我感觉文章的逻辑有点乱。如果没明白,大家可以参考下面Demo源码。其实关于Widget的这个Demo其实早在几个月前就已经写好了,但由于最近项目紧再加上本身也是第一次接触Widget控件,因此直至近日才开始动笔写这篇文章。所以文章中避免不了有错误和不合理的地方,欢迎留言指正。

    参考
    https://developer.android.com/guide/topics/appwidgets/

    源码下载

    开源库推荐

    BannerViewPager

    一个基于ViewPager2实现的具有强大功能的无限轮播库。支持多种页面切换效果和指示器样式。

    ViewPagerIndicator

    一个适用于ViewPager和ViewPager2的指示器,支持多种滑块样式及滑动模式

    展开全文
  • Widget的简单使用详解

    千次阅读 2019-10-31 18:29:18
    Widget是安卓的一个桌面小工具组件—窗口小部件,是微型应用程序视图,可以嵌入到其他应用程序(如主屏幕)和接收定期更新。 使用步骤: 1、创建一个类继承AppWidgetProvider 并重写相应方法 默认实现了onReceive ...

    Widget是安卓的一个桌面小工具组件—窗口小部件,是微型应用程序视图,可以嵌入到其他应用程序(如主屏幕)和接收定期更新。

    使用步骤:

    1、创建一个类继承 AppWidgetProvider  并重写相应方法 默认实现了onReceive 方法。

    2、在清单文件进行注册。

    3、在res目录下新建xml文件夹 配置widget相关信息。

    4、创建widget展示布局。

    5、创建widget的配置文件(可选)。

    6、更新数据。

     

    大概就是上述的一些步骤。如果在添加的widget之前需要进行一些配置 则需要实现第5步。

     

    第一步:创建类继承AppWidgetProvider   

     

    public class MyWidget extends AppWidgetProvider {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            super.onReceive(context, intent);
        }
    
        /**
         * 第一个widget被添加调用
         * @param context
         */
        @Override
        public void onEnabled(Context context) {
            super.onEnabled(context);
    
            context.startService(new Intent(context, WidgetService.class));
    
        }
    
        /**
         * widget被添加 || 更新时调用
         * @param context
         */
        @Override
        public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
            super.onUpdate(context, appWidgetManager, appWidgetIds);
            context.startService(new Intent(context, WidgetService.class));
        }
    
        /**
         * 最后一个widget被删除时调用
         * @param context
         */
        @Override
        public void onDisabled(Context context) {
            super.onDisabled(context);
            context.stopService(new Intent(context, WidgetService.class));
        }
    
        /**
         * widget被删除时调用
         * @param context
         * @param appWidgetIds
         */
        @Override
        public void onDeleted(Context context, int[] appWidgetIds) {
            super.onDeleted(context, appWidgetIds);
        }
    
    }

     

     

    2、清单文件注册

     

            <receiver android:name=".MyWidget">
    
                <intent-filter>
                    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                </intent-filter>
    
                <meta-data android:name="android.appwidget.provider"
                    android:resource="@xml/example_appwidget_info" /> //widget的配置信息文件
    
            </receiver>


    3、创建配置信息 目录 res/xml/xxx.xml

     

     

    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:minWidth="40dp" //widget的最小宽度
        android:minHeight="40dp"//widget的最小高度
        android:updatePeriodMillis="86400000"//更新的频率  google规定最小时间为30分钟
        android:previewImage="@drawable/preview" //widget的预览图 即在添加是看到的图片 一般为logo
        android:initialLayout="@layout/example_appwidget" // widget的布局文件
        android:configure="com.example.android.ExampleAppWidgetConfigure" //widget配置activity
        android:resizeMode="horizontal|vertical" //widget是否可以水平 竖直缩放 
        android:widgetCategory="home_screen|keyguard"> //widget可以被添加主屏幕或者锁屏
    </appwidget-provider>

     

    以上这些信息并不是必须  是可以选择的。updatePeriodMillis的时间最小限定在了30分钟,对于有些app来说可能时间太久了,那么我们可以将updatePeriodMillis的时间设置为0,然后通过自定义service去更新widget。

    Demo中的配置信息如下:

     

    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:initialLayout="@layout/example_appwidget"
        android:minHeight="80dp"
        android:minWidth="250dp"
        android:previewImage="@mipmap/ic_launcher"
        android:updatePeriodMillis="0"
        android:widgetCategory="home_screen|keyguard">
    
    </appwidget-provider>


    4、创建widget的展示布局,这个就根据设计,自己去码布局了。

     

    Demo中布局文件内容如下:

     

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">
    
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@mipmap/bg_icon"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:paddingLeft="30dp"
            android:paddingRight="30dp">
    
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:orientation="vertical">
    
                <TextView
                    android:id="@+id/tv_date"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#000"
                    android:textSize="16sp" />
    
                <TextView
                    android:id="@+id/tv_money"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#000"
                    android:textSize="16sp" />
    
            </LinearLayout>
    
            <Button
                android:id="@+id/btn_refound"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@null"
                android:gravity="right|center"
                android:text="点击" 
                android:textColor="#000"
                android:textSize="16sp" />
    
        </LinearLayout>
    
    </LinearLayout>


    就是简单的来个TextView来显示文案。

     

    Demo中不需要对在添加widget进行配置所以不需要第5步。

    这样简单的widget使用就完成了。

     

    最后就是更新widget数据了,为了满足自定义时间可以对widget进行更新,我们采取使用service的方式进行。同样,创建类继承Service。

    Demo中的service代码如下:

     

    public class WidgetService extends Service {
    
        private Timer mTimer;
        private SimpleDateFormat mFormat;
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            mFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
            mTimer = new Timer();
            mTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    updateWidget(WidgetService.this);
                }
            }, 0, 5000); 
    
        }
    
        private void updateWidget(Context context) {
    
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.example_appwidget);
            long millis = System.currentTimeMillis();
            String format = mFormat.format(new Date(millis));
            remoteViews.setTextViewText(R.id.tv_date, "日  期:" + format);
            remoteViews.setTextViewText(R.id.tv_money, "毫秒值:" + millis);
    
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, new Intent(this, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.btn_refound, pendingIntent);
    
            ComponentName componentName = new ComponentName(this, MyWidget.class);
            AppWidgetManager.getInstance(this).updateAppWidget(componentName, remoteViews);
    
        }
    
    
        @Override
        public void onDestroy() {
            super.onDestroy();
    
            mTimer.cancel();
            mTimer = null;
    	stopForeground(true);
    
        }
        
    }	stopForeground(true);
    
        }
        
    }


    在service的oncreate方法中创建一个Timer定时器,这个定时器每个5秒钟就会执行updateWidget方法,在updateWidget方法里面实现了具体的更新widget的逻辑。通过RemoteViews去加载布局文件 在通过setTextViewText等方法实现对控件的控制。

     

    这个地方使用了PendingIntent  ,PendingIntent 其实和Intent效果是一样的,都是充当信使的作用,但是区别就是 PendingIntent 定义的事件是提前预知的,就是不知道事件什么时候发生,但是只要发生了,就要执行PendingIntent 定义的逻辑。

    最后通过AppWidgetManager.getInstance(this).updateAppWidget(componentName, remoteViews);去更新widget组件的信息。

     

    这样看,大概一个widget的功能就已经完成了,创建了,数据也可以更新了,但是有一个问题就是,我们使用的是service去更新widget,那么我们创建的service是一个后台进程,后台进程的优先级比较低,当手机内存不足的时候 就会优先kill掉这些进程。这个时候,widget在桌面上就不会得到更新了。那么怎么解决呢? 

    这里的思路是提升service进程的优先级,将其提升为前台进程,这样就可以最大程度的保证service不会被系统kill掉了。

    在service的oncreate方法中创建一个通知,这样就将此service由后台进程变为前台进程了。代码如下:

     

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
        private void improvePriority() {
            PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, WidgetService.class), 0);
            Notification notification = new Notification.Builder(this)
                    .setContentTitle("Foreground Service")
                    .setContentText("Foreground Service Started.")
                    .setSmallIcon(R.mipmap.ic_launcher).build();
            notification.contentIntent = contentIntent;
            startForeground(1, notification); 
        }


    在oncreate方法中调用此方法即可。 Demo源码中startForeground(1, notification);  第一个参数写的是0  这个是不行的  谷歌文档提示 must not be 0 , 写0之后就not working了。但是这样,当SDK<18时,通知栏不会显示该通知,当SDK>=18时,通知栏就会显示该通知 了。关于这个问题请移步到消除前台进程通知 本人未做实践,不过的确是一种思路。

     

     

    这样基本就完成了widget的创建了。这个时候可能又会问了,万一此时service还是被kill了怎么办呢?

    一种方式是我们重写service的onStartCommand方法 并返回START_STICKY,这样如果是系统自动将service kill了,在一段时间后 在内存比较充裕的心情下 系统会自动启动这个service的。

     

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            return START_STICKY;
        }

     

     

     

     

     

    另外一种方式就是通过广播来监听此service,安卓系统提供了一个时间改变的广播接受者— ACTION_TIME_TICK 每隔一分钟 就会发送一个广播。由于此广播是系统级别的广播,所以不可以通过清单文件配置监听,只能通过代码动态的创建监听。

    创建一个类继承BroadcastReceiver 并且重写onReceive 方法 ,这样每隔一分钟 onReceive方法就会执行一次,在onReceive方法中判断service是否已经在运行,如果没有在运行就去开启service,否则就上面操作都不需要做。

     

    public class WidgetBroadcastReceiver extends BroadcastReceiver {
    
        public static final String SERVICE_NAME = "com.ppdai.widgetdemo.WidgetService";
    
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(Intent.ACTION_TIME_TICK)) {
                if (!isServiceWork(context, SERVICE_NAME)) {
                    context.startService(new Intent(context, WidgetService.class));
                }
            }
        }
    
        /**
         * 判断service是否在运行
         * @param mContext
         * @param serviceName
         * @return
         */
        public boolean isServiceWork(Context mContext, String serviceName) {
            boolean isWork = false;
            ActivityManager myAM = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
            List<ActivityManager.RunningServiceInfo> myList = myAM.getRunningServices(100);
            if (myList.size() <= 0) {
                return false;
            }
            for (int i = 0; i < myList.size(); i++) {
                String mName = myList.get(i).service.getClassName().toString();
                if (mName.equals(serviceName)) {
                    isWork = true;
                    break;
                }
            }
            return isWork;
        }
    }


    然后在需要的地方 动态的注册此广播:

     

     

            WidgetBroadcastReceiver mReceiver = new WidgetBroadcastReceiver();
            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_TIME_TICK);
            registerReceiver(mReceiver, filter);


    这样 整个widget的功能就基本完善了。如有不足 请指正! 谢谢

     

    我是源码

    扫描下方二维码关注公众号,及时获取文章推送

     

     

    展开全文
  • widget窗口小部件

    千次阅读 2016-05-19 17:15:09
    Appwidget就是手机应用中常常放在桌面(即home)上的一些应用程序,比如说闹钟等。这种应用程序的特点是它上面显示的内容能够根据系统内部的数据进行更新,不需要我们进入到程序的内部去,比如说闹钟指针的摆动等。 1...
  • QWidget

    万次阅读 多人点赞 2017-06-06 19:13:12
    QWidget最近在学习Qt,每次阅读官方英文API都只读取自己需要的一部分,但是面对庞大的英文,老是觉得获取信息像大海捞针一样困难。于是我决定从头到尾好好读一读英文介绍,然后根据自己的理解,把它翻译为中文,一...
  • PyQt5基本控件详解之QWidget(二)

    万次阅读 2018-08-11 19:43:30
    QWidget 详解内容 - 两种常用的几何结构 不包含边框的常用函数 包含边框的常用函数 实例 :一屏幕坐标系统显示 实例二:设置程序图标 实例三:显示气泡提示信息 ... 这两个函数改变了...
  • AppWidget:基本使用

    千次阅读 2017-03-21 17:42:27
    App Widget是应用程序窗口小部件(Widget)是微型的应用程序视图,它可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新。你可以通过一个App Widget Provider来发布一个Widget。 本文参考Android官方文本,...
  • 这几天做桌面widget,现将所涉及内容整理分享给大家。写博客时间长了,觉得只是把学到的知识写出来才算是已经学会了的。这也算是强迫症了吧。今天头有点痛,心情不太好,就不多请废话了,估计也没几个人会看我的前言...
  • Widget

    千次阅读 2012-07-12 13:57:31
    Web Widget,中文译名被...它的根本思想来源于代码复用,通常情况下,Widget的代码形式包含了DHTML,JavaScript以及Adobe Flash。 目录 简介作用以及批评Widget引擎Widget技术释义简介起源与发展 展开
  • 通过以上的步骤我们简单的实现了一个widget的开发步骤,用于初学者学习和交流,比较复杂的widget逻辑我们会加入网络访问,一些sevice进行数据交互,如果想要widget实现自动加入到桌面,或者widget支持自定义控件的话...
  • App Widget

    千次阅读 2015-11-28 17:08:28
    1. 基本信息App Widget是一种可以被放在其他应用中(如Launcher)并接收周期性更新的应用视图。这些视图在UI上就表现为Widget,并且你可以同App Widget Provider一起发布。 要创建一个App Widget,你需要完成以下...
  • 这两天在解一个关于widget的CR,由于之前也没有看过widget,借这个机会学习下widget,不过这个bug后来是另外一个同事fix的,这篇文章分为两部分:第一部分,分析android widget的添加过程,第二部分,分析该问题 ...
  • Today Widget介绍

    千次阅读 2016-09-22 16:16:34
    简介:Today Widget是App Extension的一种,作用是用户在使用iOS或者Mac OS下拉功能时,能够刷新显示一些用户关心的消息,比如看天气状况,查看股票行情,而且Today Widget能实现一些很小的功能,其实可以把他看成是...
  • 本文介绍了Flutter应用程序中Widget,State,BuildContext和InheritedWidget的重要概念。 特别注意InheritedWidget,它是最重要且记录较少的小部件之一。 难度:初学者 前言 Flutter中Widget,State和BuildContext的...
  • Android App Widget 开发

    千次阅读 2017-09-08 14:04:13
    概述App Widget是应用程序窗口小部件(Widget),是微型的应用程序视图,它可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新。你可以通过一个App Widget Provider来发布一个WidgetWidget布局appwidget-...
  • 前言:其实桌面widget桌面交互也是一个大坑,我在开发中遇到了真是一个又一个的问题,其中问题很多,主要原因还是android对widget的支持不够完美,比如remoteView传送数据太大就会崩等,无缘无故widget不响应等等,...
  • 前言:Flutter系列的文章我应该会持续更新至少一个月左右,从User Interface(UI)到数据相关(文件、数据库、网络)再到Flutter进阶(平台特定代码编写、测试、插件开发等),欢迎感兴趣的读者...Stateless Widget和S...
  • Wordpress学习制作Widget

    千次阅读 2016-10-12 09:56:31
    这篇虽然是全英的,但是阅读的难度系数并不大,看完基本能够知道wordpress里的Widget的制作方式,更重要的是,它给出了一些变量的意义,这是在别的教程里找不到的,所以转载过来。原文地址: ...
  • widget jquery 理解

    千次阅读 2017-06-14 17:59:33
    jquery ui 的所有组件都是基于一个简单,可重用的widget。 这个widget是jquery ui的核心部分,实用它能实现一致的API,创建有状态的插件,而无需关心插件的内部转换。 $.widget( name, base, prototype ) widget...
1 2 3 4 5 ... 20
收藏数 408,831
精华内容 163,532
关键字:

widget