1. Nimm jetzt an unserem Uhans - 3. ADVENT - Gewinnspiel teil - Alle Informationen findest Du hier!

Notizapp mit Widget, formatiertem Text, eingefärbten Buttons, Speichern&Ausgabe Text

Dieses Thema im Forum "Android Codeschnipsel" wurde erstellt von moppelg, 16.05.2010.

  1. moppelg, 16.05.2010 #1
    moppelg

    moppelg Threadstarter Android-Experte

    Beiträge:
    751
    Erhaltene Danke:
    155
    Registriert seit:
    24.11.2009
    Hallo zusammen,

    habe mich als Newbie mal 5 Tage am Stück hingesetzt und mein Gesellenstück zusammengeschustert. Ich werde hier die nächsten Tage [ist nun geschehen Anm.Red.] noch ausführlicher auf die einzelnen Besonderheiten des Programms eingehen.
    Habe die einzelnen Stücke alle aus diversen Quellen im Internet zusammengesetzt, also Dank an alle die bisher auch Tutorials und Codes veröffentlich haben.

    Hoffe, dass auch der ein oder andere an der App gefallen findet. Ich habe sie mit dem Hintergrund programmiert, weil ich eine Notiz App vermisst habe, die einfach nur von mir Geschriebenes rein als Text auf dem Desktop anzeigt. Die .apk wurde für API Level 7, sprich 2.1 programmiert. Müsste als ganzes aber auch auf API Level 5 laufen. Ob dazu jedoch ein Neukompilieren und Ändern des Manifests nötig ist weiß ich nicht.

    Es steht jedem frei das Ding zu verändern und damit zu machen was ihm beliebt. Ich habe auch vor es mal weiterzuentwickeln, so dass in einem Feld einfach Kontakte hinzugefügt werden können (z.B. auch aus dem Adressbuch) die dann unter muss "muss zurückgerufen bzw. angemailt werden). Aber das ist noch Zukunftsmusik. Ich muss jetzt erst einmal liegengelassenes erledigen...

    Anleitung zur Benutzung des Widgets:
    Das Widget startet mit einem Kästchen Höhe und vier Kästchen Breite unsichtbar auf dem Desktop. Zum Starten dann an die Stelle klicken, an die das Widget gepflanzt wurde und dann im erscheinendem Fenster seinen Text eingeben.

    Kleiner Tipp für die hardcore transparenten unter euch, es gibt auch transparente Schrift zur Auswahl, d.h. man kann auch nen Titel benutzen der nicht auf dem Desktop angezeigt wird (die "unsichtbare Widget" Warnung bleibt dann aber [fälschlicherweise:rolleyes:] aus).

    Gruß Moppel

    P.s.: Man kann über Menü die Widgetschrift anpassen

    Codes:
    Ich geh einfach mal meinen Code durch und klaub mir das ein oder andere raus. Vieles ist sicher trivial, aber ich hätte mich als Einsteiger drüber gefreut. Für die größeren Zusammenhänge empfiehlt es sich, einen Blick in den Code zu werfen:

    Zeigt ne Toast-Messege an. Tauch ne Fehlermeldung auf, muss vermutlich noch was importiert werden:
    Code:
    Toast.makeText(context, "Achtung, durchsichtiges Notiz-Widget vorhanden...", Toast.LENGTH_SHORT).show();
    Lädt sich ne View z.B. vom Widget (Knopf, Text etc.). Diese kann anschließend bearbeitet werden (Hier einen Intend anbinden, Textfarbe und Inhalt ändern):

    Code:
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
    remoteViews.setOnClickPendingIntent(R.id.button_two, configPendingIntent);
    remoteViews.setTextViewText(R.id.button_two, spannedText);    
    remoteViews.setTextColor(R.id.button_two, textColor);    
    Zugriff auf Funktionen die sich in einer eigenen Klasse im selben Package befinden. ClickOneActivity ist dabei meine Klasse.
    Code:
    String text   = ClickOneActivity.loadTitlePref(context, appWidgetId);
    SetSpan formatiert einen Text so, dass er ne ander Farbe, Style oder font erhält:
    Code:
    String font   = "Hallo, ich werde gleich formatiert";        
    SpannableString spannedText = new SpannableString(text);
    spannedText.setSpan(new StyleSpan(Typface.BOLT), 0, spannedText.length(), 0);
    spannedText.setSpan(new TypefaceSpan("monospace"), 0, spannedText.length(), 0);
    Das lässt sich auch anders implementieren:
    Code:
    String html =  "Alles löschen" + "<br />" + "<small>" + "(Gedrückt halten)" + "</small>"; 
    Button deleteall     = (Button) findViewById(R.id.deleteall);
    deleteall.setText(Html.fromHtml(html), TextView.BufferType.SPANNABLE);
    Weist einem Knopf auf dem Widget einen Intent zu. Diesem gebe ich noch eine "URI", damit er von den gleichen Knöpfen anderer, von mir erstellten, Notiz-Widgets unterschieden werden kann.
    Code:
    Intent configIntent = new Intent(context,  ClickOneActivity.class );
    configIntent.setAction( ACTION_WIDGET_CONFIGURE );
    configIntent.putExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId );
    Uri data = Uri.withAppendedPath(Uri.parse("ButtonWidget://widget/id/#"+appWidgetId), String.valueOf(appWidgetId));
    configIntent.setData(data);
    PendingIntent configPendingIntent = PendingIntent.getActivity(context, 0, configIntent, 0);
            
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
    remoteViews.setOnClickPendingIntent(R.id.button_two, configPendingIntent);
    Nach Aufruf von onUpdate soll jedes Widget geupdatet werden:
    Code:
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) 
        {
            final int N = appWidgetIds.length;
            for (int i=0; i<N; i++) 
            {
                int appWidgetId = appWidgetIds[i];      
                updateAppWidget(context, appWidgetManager, appWidgetId);
            }
        }
    Um farbige Buttons zu haben, eruierte ich zwei Wege. Einer ist sich einen Button selber zusammenzustellen in einer xml z.B.: es gibt noch viel mehr Optionen wie Farbgradienten etc. Die xml sollte im drawable Ordner liegen.
    Code:
    <?xml version="1.0" encoding="utf-8"?>
    <selector
        xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_pressed="true" >
            <shape>
                <solid 
                    android:color="@drawable/green"/> 
                <corners
                    android:radius="5dp" />
                <padding
                    android:left="5dp"
                    android:top="10dp"
                    android:right="5dp"
                    android:bottom="10dp" />
            </shape>
        </item>
    
        <item
            android:state_pressed="false">
            <shape>
                <solid 
                    android:color="@drawable/red"/> 
                <corners
                    android:radius="5dp" />
                <padding
                    android:left="5dp"
                    android:top="10dp"
                    android:right="5dp"
                    android:bottom="10dp" />
            </shape>
        </item>
    </selector>
    Da ich das zu mühselig fand, habe ich die Standartbuttons genommen und einen Farblayer drübergelegt. Sollen sie transparent sein, kann dies auch noch hinzugefügt werden:
    Transparenz:
    Code:
    Drawable d;        
    d= findViewById(R.id.confirm).getBackground();
    d.setAlpha(200);
    Farbfilter:
    Code:
    PorterDuffColorFilter filter;
    Drawable d;
    d= findViewById(R.id.deleteall).getBackground();  
    filter = new PorterDuffColorFilter(Color.argb(100, 255, 0, 0), PorterDuff.Mode.SRC_ATOP);  
    d.setColorFilter(filter);  
    Jedoch ist der Farbfilter auch bei gedrückter Taste aktiv. Damit ich dort aber das normale grün der Standartbuttons habe implementierte ich noch einen Touchlistener auf den gleichen Button, der bei aktivwerden den Filter wegnimmt:

    Code:
    Button confirmButton = (Button) findViewById(R.id.confirm);
    confirmButton.setOnClickListener(mConfirm);  
    confirmButton.setOnTouchListener(mConfirmOnTouch);    
    Code:
    private OnTouchListener mConfirmOnTouch = new OnTouchListener() 
        {        
            @Override
            public boolean onTouch(View v, MotionEvent event) 
            {
                if(event.getAction() == MotionEvent.ACTION_DOWN)
                {
                    Drawable d = findViewById(R.id.confirm).getBackground();  
                    findViewById(R.id.confirm).invalidateDrawable(d);  
                    d.clearColorFilter();
                }
                else
                {
                    setButtonFilter();
                }
                return false;
            }
        };
    Es sei noch dazugesagt, dass diese Methode einen klitzekleinen Schönheitsfehler hat, der jedoch zu 99,9999% nicht auffällt. Wird die Taste gedrückt ist sie im normalen grün. Verlässt man bei gedrücktem Screen schnappt die Farbe wieder zurück, was ja auch soll. Slide ich dann jedoch wieder drauf, wird nicht mehr MotionEven.ACTION_DOWN aufgerufen und der Filter bleibt drinne, was in einer anderen Farbe resultiert. Leider habe ich keinen Event gefunden, die feststellt, wann der eigentliche View, sprich Button verlassen wird. Blöd mit x und y rumrechnen wollte ich auch nicht. Dafür ist der "Fehler" zu unauffällig.

    Text und Zahlen speicher ich in der Preferenceclasse ab. Das ist mehr als einfach. Zum speichern:
    Code:
    SharedPreferences.Editor prefs = context.getSharedPreferences("Einstellungen", 0).edit();
    prefs.putString("title" + appWidgetId, text);
    prefs.commit();
    Die ID füge ich noch an den Speichername an, damit das jeweilige Widget seinen Text oder Int abrufen kann.
    Code:
    Zum laden des Textes, bzw. ints:
    SharedPreferences prefs = context.getSharedPreferences("Einstellungen", 0);
    String font = prefs.getString("font" + appWidgetId, "serif");
    Meinen AltertDIalog zum abfragen des Schriftfonts habe ich so gestaltet:
    Code:
    private void dialogStyle() 
         {     
             
             final CharSequence[] items = {"Fett", "Fett & Kursiv", "Kursiv", "Normal"};
             DialogInterface.OnClickListener mOnClick = new DialogInterface.OnClickListener()
             {
                    @Override
                    public void onClick(DialogInterface dialog, int item) 
                    {
                         final Context context = ClickOneActivity.this;
                         SharedPreferences.Editor prefs = context.getSharedPreferences("Einstellungen", 0).edit();
                         switch (item) 
                         {    
                             case 0: prefs.putInt("style" + mAppWidgetId, Typeface.BOLD); break;
                             case 1: prefs.putInt("style" + mAppWidgetId, Typeface.BOLD_ITALIC); break;
                             case 2: prefs.putInt("style" + mAppWidgetId, Typeface.ITALIC); break;
                             case 3: prefs.putInt("style" + mAppWidgetId, Typeface.NORMAL); break;
                          }                       
                         prefs.commit();
                         Toast.makeText(context, "\"" + items[item] + "\" gewählt", Toast.LENGTH_SHORT).show();
                    }          
              }; 
             AlertDialog.Builder builder = new AlertDialog.Builder(this);
             builder.setTitle("Wähle einen Widgetschriftstyle:");
             builder.setItems(items, mOnClick);
            
             AlertDialog alert = builder.create();
             alert.show();
        }
    zum Laden des gespeicherten wie oben erwähnt:
    Code:
    SharedPreferences prefs = context.getSharedPreferences("Einstellungen", 0);
    String font = prefs.getString("font" + appWidgetId, "serif");
    Ich vermute mal, dass das in der Preference gespeicherte beim löschen des Widgets auch entfernt werden soll. Daher das überschreiben von OnDestroy:
    Code:
    @Override
        public void onDeleted(Context context, int[] appWidgetIds) 
        {
            Toast.makeText(context, "Deleting Widget...", Toast.LENGTH_SHORT).show();
            // When the user deletes the widget, delete the preference associated with it.
            final int N = appWidgetIds.length;
            for (int i=0; i<N; i++) 
            {
                ClickOneActivity.deleteTitleAndBodyPref(context, appWidgetIds[i]);
            }
        }
    Ich habe mich immer geärgert, dass wenn die Hometaste gedrückt wird die Activity nicht geschlossen wird. Denn bleibt sie offen und ich drücke andere Widgets vom Typ des NotiWidgets, dann öffnen sich diese Fenster. Drücke ich dann "zurück", lande ich plötzlich im alten Fenster und visa versa. Auf jeden Fall Käse. Einen finish() in onPause ist aber auch keine Lösung, weil bei der ScreenRotation diese aufgerufen wird. Dann möchte ich nicht, dass die Activity geschlossen wird. Geholfen hat folgender Eintrag im Manifest der Activity:
    android:configChanges="orientation">
    Dann wird beim Screenwechseln nicht onPause aufgerufen und ich kann dort in Ruhe mein finish() reinschreiben:
    Code:
    @Override
         protected void onPause() 
         {
                super.onPause();
                final Context context = ClickOneActivity.this;  
                saveTitlePref( context, mAppWidgetId, mTitleText.getText().toString());
                saveBodyPref(  context, mAppWidgetId,  mBodyText.getText().toString());
                AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
                ButtonWidget.updateAppWidget(context, appWidgetManager, mAppWidgetId);
                finish();
          }
    So sieht das bei mir im Code auf, ich speicher alles ab und update das Widget.

    Vollständigkeithalber noch wie ich das Optionsmenü aufrufe:
    public boolean onCreateOptionsMenu(Menu menu)
    Code:
       {
            super.onCreateOptionsMenu(menu);
            MenuInflater mi =
            new MenuInflater(getApplication());
            mi.inflate(R.menu.menu, menu);
            return true; 
        }
        
        public boolean onOptionsItemSelected(MenuItem item) 
        {
            switch (item.getItemId()) 
            {        
            case R.id.schriftfarben: 
                dialogSchriftfarben();
                return true;
            case R.id.schriftstyle: 
                dialogStyle();
                return true;
            case R.id.schriftfont: 
                dialogFont();
                return true;
            }
            return super.onContextItemSelected(item);
        }
    Die XML dazu:
    Code:
    <?xml version="1.0" encoding="utf-8"?>
    <menu
          xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:id="@+id/schriftfarben"
            android:title="Schriftfarbe"
            />
        <item
            android:id="@+id/schriftstyle"
            android:title="Schriftstyle"/>
            />
        <item
            android:id="@+id/schriftfont"
            android:title="Schriftfont"/>
            />
    </menu>
    Damit das Programm die Wallpaper im Hintergrund hat, hier noch mein Manifesteintrag:
    Code:
    <activity android:name=".ClickOneActivity"                  
                      android:theme="@style/Theme.Wallpaper"
                      android:configChanges="orientation">
                <intent-filter>
                    <action android:name="de.thesmile.android.widget.buttons.ButtonWidget.ACTION_WIDGET_CONFIGURE"/>
                </intent-filter>
            </activity>
    Dazu wird noch die styles.xml benötigt im values Ordner:
    Code:
    <resources>
        <!-- Base application theme is the default theme. -->
        <style name="Theme" parent="android:Theme">
        </style>
    
        <style name="Theme.Black">
            <item name="android:windowBackground">@drawable/screen_background_black</item>
        </style>
    
        <style name="Theme.Wallpaper" parent="android:style/Theme.Wallpaper">
            <item name="android:colorForeground">#fff</item>
        </style>
    
        <style name="Theme.Translucent" parent="android:style/Theme.Translucent">
            <item name="android:windowBackground">@drawable/translucent_background</item>
            <item name="android:windowNoTitle">true</item>
            <item name="android:colorForeground">#fff</item>
        </style>
    
        <style name="Theme.Transparent">
            <item name="android:windowIsTranslucent">true</item>
            <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
            <item name="android:windowBackground">@drawable/transparent_background</item>
            <item name="android:windowNoTitle">true</item>
            <item name="android:colorForeground">#fff</item>
        </style>
        
        <style name="TextAppearance.Theme.PlainText" parent="android:TextAppearance.Theme">
            <item name="android:textStyle">normal</item>
        </style>
        
    </resources>
    In der XML oben sind auch noch andere Themes, die alternativ benutzt werden könnten (der scharfsinnige Leser wird sehen, dass die Beispiele von Google sind:)

    Die Titel Leiste in der Activity bekomme ich weg mit:
    Code:
    @Override
        protected void onCreate(Bundle icicle) 
        {
            super.onCreate(icicle);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
    ...
    Trotz dem sind mir noch zwei Bugs bekannt, vielleicht weiß ja jemand Abhilfe:
    - Wenn ich bei Details ins Textfeld nur eine E-Mailadresse reinschreibe und die nach Wiederaufruf der Acktivity blau markiert wird ist es nahezu ein Ding der Unmöglichkeit irgendwie in das Textfeld zu klicken um Text hinzuzufügen. Der Link wird immer magisch angeklickt.
    - Nach löschen aller Widgets vom Homescreen kann je nach Fall noch in der History eine Activity erscheinen. Bei Aufruf dieser lässt sich wieder Text eingeben und speichern, obwohl das dazugehörige Widget weg ist. Gibt es ne Möglichkeit eine App aus der History rauszunehmen?

    !Achtung!
    Es kann zum Verlust/Absturz der Notiz kommen kann, wenn bei "Titel" eine lange Nummer oder E-Mailadresse eingegeben wird. So war es bei mir zumindest unter Home++. Ich vermute, dass es damit zusammenhängt, dass ich für das Textfeld ebenso wie beim Detailtextfeld die automatische Telefonnummern- und Emailadressenerkennung aktiviert habe. Warum das beim einen Feld funktioniert und beim anderen nicht ist mir dabei unklar. Hoffe, dass ihr nicht schon vorher die böse Erfahrung gemacht haben... . Wenn es stört bitte bei euch dann selber im Quellcode korrigieren und Neukompilieren
     

    Anhänge:

    Zuletzt bearbeitet: 26.09.2010
    myLG, PauleFlügge, SeraphimSerapis und 2 andere haben sich bedankt.
  2. SeraphimSerapis, 25.05.2010 #2
    SeraphimSerapis

    SeraphimSerapis Android-Guru

    Beiträge:
    3,072
    Erhaltene Danke:
    1,138
    Registriert seit:
    27.02.2009
    Sehr nett! Danke für das gute Beispiel!
     
    Chrischi2809 und moppelg haben sich bedankt.
  3. Chrischi2809, 01.06.2010 #3
    Chrischi2809

    Chrischi2809 Android-Hilfe.de Mitglied

    Beiträge:
    58
    Erhaltene Danke:
    1
    Registriert seit:
    20.05.2009
    Phone:
    Samsung Galaxy S4 Mini
    Habe es auf meinem HTC Desire installiert.
    Wenn ich das Widget auf dem Destkop anlege, ist das widget durchsichtig. Auf dem desktop ist nichts zu sehen... klicke einfach ins leere bis es sich öffnet...
     
  4. moppelg, 02.06.2010 #4
    moppelg

    moppelg Threadstarter Android-Experte

    Beiträge:
    751
    Erhaltene Danke:
    155
    Registriert seit:
    24.11.2009
    Hey Chrischi, danke, dass du es nochmal ansprichst. War etwas unklar dokumentiert von mir.
    Wie Chrischi bereits sagte startet das Widget mit einem Kästchen Höhe und vier Kästchen Breite unsichtbar auf dem Homescreen. Zum Starten dann an die Stelle klicken, an die das Widget gepflanzt wurde und seinen Text eingeben.
    Es sind auch mehrere Widgets möglich.

    Kleiner Tipp für die hardcore transparenten unter euch, es gibt auch transparente Schrift zur Auswahl, d.h. man kann auch nen Titel benutzen der nicht auf dem Desktop angezeigt wird (die "unsichtbare Widget" Warnung bleibt dann aber [fälschlicherweise:rolleyes:] aus).
     
    Zuletzt bearbeitet: 02.06.2010
  5. Mischor, 21.08.2010 #5
    Mischor

    Mischor Android-Hilfe.de Mitglied

    Beiträge:
    74
    Erhaltene Danke:
    3
    Registriert seit:
    08.04.2010
    Phone:
    HTC Desire
    Nice genau sowas hab ich gesucht :D
    Sach mal ist es noch möglich eine Funktion für Fotos ein zu fügen?
    Sprich dass man zu seiner Notiz ein Foto machen kann und es klein auf der rechten oder linken Seite angezeigt wird :)
    Und wenn man auf das Bild drückt, dass es in einem "popup" in groß angezeigt wird.
     
  6. moppelg, 26.09.2010 #6
    moppelg

    moppelg Threadstarter Android-Experte

    Beiträge:
    751
    Erhaltene Danke:
    155
    Registriert seit:
    24.11.2009
    Hi Mischor,
    danke für dein Interesse, aber leider weiß ich nicht wann ich mich mal wieder ransetze um mit Android weiterzuprogrammieren.

    !Achtung!
    Möchte aber noch meine Antwort nutzen um eine kleine Warnung rauszugeben, dass es zum Verlust/Absturz der Notiz kommen kann, wenn bei "Titel" eine lange Nummer oder E-Mailadresse eingegeben wird. So war es bei mir zumindest unter Home++. Ich vermute, dass es damit zusammenhängt, dass ich für das Textfeld ebenso wie beim Detailtextfeld die automatische Telefonnummern- und Emailadressenerkennung aktiviert habe. Warum das beim einen Feld funktioniert und beim anderen nicht ist mir dabei unklar. Hoffe, dass ihr nicht schon vorher die böse Erfahrung gemacht haben...
     

Diese Seite empfehlen