M
moppelg
Stamm-User
- 166
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] 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:
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):
Zugriff auf Funktionen die sich in einer eigenen Klasse im selben Package befinden. ClickOneActivity ist dabei meine Klasse.
SetSpan formatiert einen Text so, dass er ne ander Farbe, Style oder font erhält:
Das lässt sich auch anders implementieren:
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.
Nach Aufruf von onUpdate soll jedes Widget geupdatet werden:
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.
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:
Farbfilter:
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:
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:
Die ID füge ich noch an den Speichername an, damit das jeweilige Widget seinen Text oder Int abrufen kann.
Meinen AltertDIalog zum abfragen des Schriftfonts habe ich so gestaltet:
zum Laden des gespeicherten wie oben erwähnt:
Ich vermute mal, dass das in der Preference gespeicherte beim löschen des Widgets auch entfernt werden soll. Daher das überschreiben von OnDestroy:
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:
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)
Die XML dazu:
Damit das Programm die Wallpaper im Hintergrund hat, hier noch mein Manifesteintrag:
Dazu wird noch die styles.xml benötigt im values Ordner:
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:
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
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] 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();
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);
Code:
String text = ClickOneActivity.loadTitlePref(context, appWidgetId);
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);
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);
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);
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);
}
}
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>
Transparenz:
Code:
Drawable d;
d= findViewById(R.id.confirm).getBackground();
d.setAlpha(200);
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);
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;
}
};
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();
Code:
Zum laden des Textes, bzw. ints:
SharedPreferences prefs = context.getSharedPreferences("Einstellungen", 0);
String font = prefs.getString("font" + appWidgetId, "serif");
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();
}
Code:
SharedPreferences prefs = context.getSharedPreferences("Einstellungen", 0);
String font = prefs.getString("font" + appWidgetId, "serif");
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]);
}
}
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();
}
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);
}
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>
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>
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>
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);
...
- 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: