Autore Topic: [Medio]App Widgets  (Letto 12632 volte)

Zate

  • Visitatore
[Medio]App Widgets
« il: 02 Ottobre 2011, 23:28:19 CEST »
+4
Dopo aver ricevuto vari aiuti, voglio dare anch'io il mio (per quanto piccolo) contributo.
Difficoltà:media
Target SDK:8
Link al progetto:allegato
Altre info:http://developer.android.com/guide/topics/appwidgets/index.html

Alcune informazioni:
_Un widget si può aggiornare non più frequentemente di 30 minuti (1800000 millisecondi) per evitare consumi di batteria abnormi.
_Bisognerà inserire minwidth e minheight del widget. Seguite questa forumla dove x = numero delle celle che si vogliono usare. (x * 74) - 2

Spero che il tutorial vada bene e che non ci sia già (se c'è non l'ho visto ;))
Credo di non aver dimenticato niente, nel caso avessi dimenticato qualcosa modificherò il post.


1)Creiamo un semplice .xml chiamato newactivity(voi potete chiamarlo come volete), con una semplice textview. Per chi non avesse voglia di scrivere, ecco qua il codice:
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView android:text="Benvenuto a NewActivity" android:id="@+id/textView1"
                android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
</LinearLayout>

2)Creiamo una classe e chiamiamola NewActivity(anche qui potete chiamarla come preferite) e settiamo il layout.
Codice (Java): [Seleziona]
package widgets.test;

import android.app.Activity;
import android.os.Bundle;

public class NewActivity extends Activity{

        @Override
        protected void onCreate(Bundle savedInstanceState) {
                // TODO Auto-generated method stub
                super.onCreate(savedInstanceState);
                setContentView(R.layout.newactivity);
        }

}

3) Modifichiamo l'Android Manifest:
Codice (XML): [Seleziona]
<activity android:name=".NewActivity" android:label="@string/app_name">
        <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
</activity>

Questa era la parte facile, adesso avventuriamoci nel widget:
4)Creiamo un file xml, ma attenzione, selezionate AppWidget Provider, non Layout. Chiamiamolo widget_settings
Potete modificare il file da Structure oppure scrivere voi il codice, nel primo caso sarà più semplice (non penso ci sia bisogno di una spiegazione per quello).
Min width (larghezza) = 72dp (Se vi state chiedendo: "Perché 72 dp?!" Leggete in alto :))
Min height (altezza) = 72dp(Stesso discorso)
Update period millis = 1800000 (1 800 000, mezz'ora. Non si possono aggiornare più frequentemente per evitare consumi di batteria troppo                     elevati)
Initial layout = @layout/widget (poi andremo a configurarlo, sarà il layout vero e proprio del widget)
Configure = widget.test.Widgets1Config (classe che andremo a creare in seguito, dove configureremo il widget)

Il codice xml è questo:
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:updatePeriodMillis="1800000" android:initialLayout="@layout/widget"
        android:minWidth="72dp" android:minHeight="72dp" android:configure="widgets.test.Widgets1Config">
</appwidget-provider>

5)Creiamo un layout e chiamiamolo "widget" (senza le virgolette). Questo sarà il layout del widget che vedremo sulla home (e che abbiamo messo in "Initial layout" in widget_settings). Non fate caso all'apparenza nella tab Graphical Layout, il widget non occuperà tutta la home perché abbiamo settato
Min width = 72dp
Min height = 72dp
Quindi occuperà solo una cella.
A widget.xml aggiungiamo una textview e un button, qui c'è il codice:
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView android:text="TextView" android:id="@+id/tvRand"
                android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
        <Button android:text="Button" android:id="@+id/bOpenActivity"
                android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
</LinearLayout>
tvRand sarà diventerà il numero casuale da 1 a 1000 e bOpenActivity aprirà l'activity dove andremo a settare il testo del button.
La parte noiosa è quasi finita, ma prima...
6) Creiamo un layout chiamato widgets1config.xml, sarà il layout dell'activity Widgets1Config (che abbiamo messo in "Configure" in widget_settings). Aggiungiamo una edittext e un button. Qui il codice:
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent">
        <EditText android:id="@+id/etConfig" android:layout_height="wrap_content"
                android:layout_width="match_parent">
                <requestFocus></requestFocus>
        </EditText>
        <Button android:layout_height="wrap_content" android:text="Button"
                android:id="@+id/bConfig" android:layout_width="match_parent"></Button>
</LinearLayout>
Il testo che si digiterà in etConfig sarà il nome del button del widget(bOpenActivity) e bConfig sarà il button per dare l'ok.

7) Modifichiamo ancora l'AndroidManifest aggiungendo:
Codice (XML): [Seleziona]
                <receiver android:name=".Widgets1" android:label="@string/app_name">
                    <intent-filter >
                        <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
                    </intent-filter>
                    <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_settings" />
                </receiver>
                <activity android:name=".Widgets1Config" android:label="@string/app_name"
                        android:screenOrientation="portrait">
                        <intent-filter>
                                <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
                        </intent-filter>
                </activity>

Abbiamo settato la classe Widgets1 come widget, e Widgets1Config come configuratore (forse qualcuno mi riesce a suggerire un termine più appropriato :D)

8) Creiamo la classe Widgets1 (che, ripeto, sarà il widget), ma anziché estendere una activity estendiamo un AppWidgetProvider.
Quindi:
Codice (Java): [Seleziona]
package widgets.test;

import java.util.Random;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.widget.RemoteViews;
import android.widget.Toast;

public class Widgets1 extends AppWidgetProvider {

        @Override
        public void onUpdate(Context context, AppWidgetManager appWidgetManager,
                        int[] appWidgetIds) {
                // TODO Auto-generated method stub
                super.onUpdate(context, appWidgetManager, appWidgetIds);
                // numero casuale tra 1 e 1000
                Random r = new Random();
                int randomInt = r.nextInt(1000);
                // trasforma integer a string, in modo da poter essere usato in una
                // textview
                String rand = "" + randomInt;
                // assegna ad N il valore di appWidgetIds.length
                final int N = appWidgetIds.length;
                // Loop per ogni widget che appartiene a questo provider
                for (int i = 0; i < N; i++) {
                        int awID = appWidgetIds[i];
                        /* Prende il layout per il widget e setta un numero casuale tra 1 e
                        * 100 (trovato sopra) come testo della textview del widget
                        */

                        RemoteViews rv = new RemoteViews(context.getPackageName(),
                                        R.layout.widget);
                        rv.setTextViewText(R.id.tvRand, rand);
                        // Dice di aggiornarsi
                        appWidgetManager.updateAppWidget(awID, rv);
                }
        }

        @Override
        public void onDeleted(Context context, int[] appWidgetIds) {
                // TODO Auto-generated method stub
                super.onDeleted(context, appWidgetIds);
                // Toast quando viene eliminato
                Toast.makeText(context, "Perchè mi abbandoni?!", Toast.LENGTH_SHORT)
                                .show();
        }

}

Penso che il codice sia piuttosto commentato, non credo ci siano bisogno di altre spiegazioni :)

10)Creiamo la classe Widgets1Config che sarà il nostro "configuratore". Anche qui ho commentato il codice ampiamente:

Codice (Java): [Seleziona]
package widgets.test;

import android.app.Activity;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RemoteViews;

public class Widgets1Config extends Activity implements OnClickListener{

        EditText info;
        AppWidgetManager awm;
        Context c;
        int awID;
       
        @Override
        protected void onCreate(Bundle savedInstanceState) {
                // TODO Auto-generated method stub
                super.onCreate(savedInstanceState);
                //operazioni di routine
                setContentView(R.layout.widgets1config);
                Button b = (Button)findViewById(R.id.bConfig);
                b.setOnClickListener(this);
                c = Widgets1Config.this;
                info = (EditText)findViewById(R.id.etConfig);
                //si prendono info circa il widget che ha lanciato questa classe
                Intent i = getIntent();
                Bundle extras = i.getExtras();
                if(extras != null){
                        //reference
                        awID = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
                }else{
                        finish();
                }
                //si relaziona il widget al context
                awm = AppWidgetManager.getInstance(c);
        }

        @Override
        public void onClick(View arg0) {
                // TODO Auto-generated method stub
        String e = info.getText().toString();
        //cambia il testo del pulsante del widget
        RemoteViews rv = new RemoteViews(c.getPackageName(), R.layout.widget);
        rv.setTextViewText(R.id.bOpenActivity, e);
       //premendo il pulsante del widget si apre un'activity
        Intent in = new Intent(c, NewActivity.class);
        PendingIntent pi = PendingIntent.getActivity(c, 0, in, 0);
        rv.setOnClickPendingIntent(R.id.bOpenActivity, pi);
       //aggiorna
        awm.updateAppWidget(awID, rv);
        Intent result = new Intent();
        result.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, awID);
        setResult(RESULT_OK, result);
       
        finish();
        }

}

E questo è tutto...
Il risultato dovrebbe essere un widget che nel momento in cui si mette sulla home apre un'activity (Widgets1Config, la classe configuratrice) dove metteremo il testo del button del widget. Ogni 30 minuti il widget si aggiornerà e setterà come testo della textview un numero casuale da 1 a 1000. Premendo il button si aprirà NewActivity. Quando si eliminerà il widget comparirà un toast (notifica, prossimamente il tutorial per far comparire veri e propri toast)
Vorrei far notare quanto sia utile questo widget durante la quotidianità ;)

ATTENZIONE: Se si sposta un'app con un widget sulla scheda sd, il widget non sarà più disponibile.

Fatemi notari gli eventuali errori da me commessi.
(Testato su un LG Optimus One con Android 2.3.4 e Android 2.3.5)
« Ultima modifica: 14 Ottobre 2011, 16:35:36 CEST da Zate »

Offline DrFred

  • Nuovo arrivato
  • *
  • Post: 5
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    xperia X10
  • Play Store ID:
    DrFred
  • Sistema operativo:
    windows 7
Re:[Medio]App Widgets
« Risposta #1 il: 27 Novembre 2011, 13:20:42 CET »
0
Ciao, come prima cosa grazie per la guida perchè in rete non si trova molto sui widget in italiano.
Ho provato ad implementare il widget ed appena installato parte NewActivity, poi però se provo ad aggiungere il widget alla home ricevo il classico messaggio di force close, cosa può essere?
Poi non ho capito bene come funziona a livello generale il meccanismo di creazione e configurazione del widget. Cioè quando si aggiunge il widget alla home cosa avviene? Quale attività viene lanciata?

Zate

  • Visitatore
Re:[Medio]App Widgets
« Risposta #2 il: 27 Novembre 2011, 13:30:48 CET »
0
Serve il LogCat ;)
Quando il widget viene posizionato sulla home viene lanciata la classe Widgets1Config perché nel manifest abbiamo messo                                 <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
Il widget invece è Widgets1. NewActivity è una semplicissima activity che viene lanciata quando si preme il pulsante del widget.

Offline DrFred

  • Nuovo arrivato
  • *
  • Post: 5
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    xperia X10
  • Play Store ID:
    DrFred
  • Sistema operativo:
    windows 7
Re:[Medio]App Widgets
« Risposta #3 il: 04 Dicembre 2011, 20:01:19 CET »
0
Ciao, ho corretto gli errori sciocchi che avevo fatto seguendo il tutorial e funziona tutto. Quindi adesso sono andato un pò avanti, non so se posso chiedere direttamente qui ma cosi magari si possono aggiungere altre funzioni oltre a quelle spiegate nel tutorial che possono essere sempre utili.
Sto facendo un widget che mostra 3 immagini prese da un database e vorrei aggiungere due bottoni che mostrano le tre immagini precedenti o successive a quelle attualmente mostrate.
La prima cosa che mi viene in mente è richiamare l'update del widget quando si preme il bottone ma in questo modo ogni volta deve riprendere tutte le immagini dal database e non credo sia molto efficiente, esiste una strada migliore?

Zate

  • Visitatore
Re:[Medio]App Widgets
« Risposta #4 il: 05 Dicembre 2011, 21:08:09 CET »
0
Be', non penso che ci sia bisogno di grandi performance per un widget. O forse sono io che non ho capito bene il problema...

Offline DrFred

  • Nuovo arrivato
  • *
  • Post: 5
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    xperia X10
  • Play Store ID:
    DrFred
  • Sistema operativo:
    windows 7
Re:[Medio]App Widgets
« Risposta #5 il: 08 Dicembre 2011, 19:24:44 CET »
0
Ciao, scusa se ci metto un po a rispondere ma non posso dedicarmi spesso ad android...l'università chiama. Cmq ho provato ad implementare un bottone che quando viene premuto mostra le tre immagini successive. Per farlo ho associato al bottone un pendingIntent, come hai mostrato tu nel  tutorial, ma invece di lanciare un'activity chiama il metodo onReceiver  che poi richiama il metodo onUpdate del widget. A questo punto riparte tutta l'interrogazione del database e la configurazione della remoteView. In questo modo però c'è un ritardo fastidioso da quando si preme il bottone a quando cambiano le immagini. La mia idea era quella di fare un array "photos[j]" che contenesse le immagini  prelevate dal database al momento della creazione del widget e quando si preme il bottone viene eseguita solamente:
remoteView.setImageViewBitmap(R.id.contactPhoto, photos[j]); (con la j opportunamente aggiornato)
Il problema è che nel metodo onReceive come faccio a passargli la remoteView? esiste un modo o sbaglio completamente strada?
« Ultima modifica: 08 Dicembre 2011, 19:28:30 CET da DrFred »

Zate

  • Visitatore
R: [Medio]App Widgets
« Risposta #6 il: 08 Dicembre 2011, 20:37:50 CET »
0
Come ho scritto, teoricamente un widget può essere aggiornato solo ogni 30 minuti (e penso che questa sia la causa del tuo ritardo). Questo limite può essere raggirato lanciando un AlarmManager.

Offline DrFred

  • Nuovo arrivato
  • *
  • Post: 5
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    xperia X10
  • Play Store ID:
    DrFred
  • Sistema operativo:
    windows 7
Re:[Medio]App Widgets
« Risposta #7 il: 09 Dicembre 2011, 20:26:33 CET »
0
Dopo un po di studio sono riuscito a fare quello che dicevo, posto la soluzione cosi può essere utile a qualcuno:
Nel metodo onUpdate del widget che viene chiamato solamente quando viene aggiunto alla home interrogo il database e salvo il risultato nell'array photo[j].
Nel metodo onReceive invece ho una cosa cosi:
Codice (Java): [Seleziona]
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
remoteViews.setTextViewText(R.id.name, names[j+1]);      
remoteViews.setImageViewBitmap(R.id.photo, photos[j+1]);

ComponentName widget = new ComponentName(context, FrequentContactWidgetActivity.class);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
appWidgetManager.updateAppWidget(widget, remoteViews);

Quello che mi traeva in inganno era
new RemoteViews(context.getPackageName(), R.layout.main);
che pensavo creasse una nuova remoteViews.

Offline andre_sghedo

  • Nuovo arrivato
  • *
  • Post: 27
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    windows 7
Re:[Medio]App Widgets
« Risposta #8 il: 21 Febbraio 2012, 15:03:19 CET »
0
a me non funziona il widget e m da errore nell @Override della funzione OnClick

Zate

  • Visitatore
R: [Medio]App Widgets
« Risposta #9 il: 21 Febbraio 2012, 15:05:15 CET »
0
Che versione del JDK stai usando?

Offline andre_sghedo

  • Nuovo arrivato
  • *
  • Post: 27
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    windows 7
Re:[Medio]App Widgets
« Risposta #10 il: 21 Febbraio 2012, 17:14:47 CET »
0
2.2

Offline andre_sghedo

  • Nuovo arrivato
  • *
  • Post: 27
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    windows 7
Re:[Medio]App Widgets
« Risposta #11 il: 21 Febbraio 2012, 17:15:56 CET »
0
a figura dell applicazione widget mi compare e quando clicco su di esso mi viene fuori la scritta "Hello,blabla.."pero dopo non fa nulla!

Zate

  • Visitatore
R: [Medio]App Widgets
« Risposta #12 il: 21 Febbraio 2012, 17:52:05 CET »
0
JDK, non targetSDK. Per vedere se funziona prova a ricompilare il progetto mentre il widget è nella home. Se compare un numero allora funziona
« Ultima modifica: 21 Febbraio 2012, 17:54:47 CET da Zate »

Offline andre_sghedo

  • Nuovo arrivato
  • *
  • Post: 27
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    windows 7
Re:[Medio]App Widgets
« Risposta #13 il: 21 Febbraio 2012, 18:59:59 CET »
0
JDK 8,mi da un errore nell @Override OnClick...mi dice di togliere il tag Override

Zate

  • Visitatore
Re:[Medio]App Widgets
« Risposta #14 il: 21 Febbraio 2012, 20:54:20 CET »
0
Forse mi sono spiegato male io. Il JDK è questo Java SE Downloads.
Per vedere quello che utilizzi vai su Window>Preferences>Java (espandilo)>Compiler. Probabilmente in compiler compliance level hai 1.5. Metti 1.6 e non dovresti avere gli errori negli Override. Per sicurezza guarda anche il compiler compliance level del progetto: tasto destro sul tuo progetto>Properties>Java Compiler.
P.S. Sei riuscito a far funzionare il widget? Cosa non funziona? Riesci ad aggiungere il testo nel pulsante? Per far comparire il numero casuale forse mi sono spiegato male: bisogna reinstallare l'applicazione. Per reinstallarla cambia qualcosa nel codice (basta anche uno spazio tra una riga e l'altra) e l'app si reinstallerà e il widget si aggiornerà.