Este widget es una broma.En este tutorial, te ayudaré … | por Aviad H | Mayo de 2021
En este tutorial, te ayudaré a crear un widget de broma para Android desde cero usando la API de broma auténtico en la «API de broma oficial» https://github.com/15Dkatz/official_joke_api.
Introduciremos los conceptos básicos de la creación de widgets a través de la pantalla de configuración y usaremos las preferencias compartidas para achivar las opciones del legatario.
Todavía usaremos Google Volley para construir un cliente HTTP simple para obtener los chistes del widget y hacerlo reponer a las interacciones del legatario.
Puede clonar el esquema desde GitHub y seguir los pasos y los ejemplos de codificación a continuación.
Tesina GitHub-https: //github.com/aviadh314/JokesWidget/tree/master
tema
*Yo soy túsestudio de Android, Java para Android (8).
Sin más presentación, comencemos.
Utilice Android Studio para crear widgets predeterminados.
- Crea un nuevo esquema sin actividad
2. Inserte «JokesWidget» en el nombre del esquema e inserte «com.my.jokeswidget» en el «nombre del paquete».
3. Haga clic con el capullo derecho en la aplicación / res: Nuevo → Widget → Widget de la aplicación
Inserte «MyWidget» como el nombre de la clase y luego seleccione la configuración, como se muestra en la sucesivo figura
Configurar el diseño de la pantalla
1. Refugio app / res / my_widget_configure.xml, agregaremos dos ordenanza para configurar el fondo del widget, umbrío o claro.
my_widget_configure.xml
<?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="wrap_content"
android:orientation="tieso"
android:gravity="center"
android:padding="16dp"><TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="20sp"
android:layout_marginBottom="8dp"
android:text="@string/configure" />
<TableRow
android:layout_height="40dp"
android:layout_width="match_parent"
android:layout_gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/btDarkMode"
android:theme="@style/Theme.AppCompat"
android:background="@drawable/rs_dark"
android:layout_height="match_parent"
android:text="@string/dark"
android:textColor="@color/white"
android:gravity="center"
android:layout_weight="1"
android:layout_margin="2dp"/>
<Button
android:id="@+id/btLightMode"
android:theme="@style/Theme.AppCompat"
android:background="@drawable/rs_light"
android:layout_height="match_parent"
android:text="@string/light"
android:textColor="@color/black"
android:layout_weight="1"
android:gravity="center"
android:layout_margin="2dp"/>
</TableRow>
<Button
android:id="@+id/btAddWidget"
android:theme="@style/Theme.AppCompat.Light"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="20sp"
android:layout_marginTop="8dp"
android:text="@string/add_widget" />
</LinearLayout>
2. Cree rs_dark y rs_light con un fondo que se pueda dibujar
rs_dark.xml (aplicación / resolución / dibujable)
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#311331" />
<stroke
android:width="1dp"
android:color="#20FFFFFF" />
<corners android:radius="20dp" />
<padding
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
android:top="5dp" />
</shape>
rs_light.xml (aplicación / resolución / dibujable)
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#60DF59F3" />
<stroke
android:width="1dp"
android:color="#20FFFFFF" />
<corners android:radius="20dp" />
<padding
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
android:top="5dp" />
</shape>
Selección de legatario de manejo de pantalla de configuración
- 1. Procese las selecciones del legatario y guárdelas localmente anejo con las preferencias compartidas
MyWidgetConfigureActivity.java
package com.my.jokeswidget;import android.app.Activity;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.core.content.ContextCompat;
public class MyWidgetConfigureActivity extends Activity
private static final String PREFS_NAME = "com.my.jokeswidget.MyWidget";
private static final String PREF_PREFIX_KEY = "appwidget_";
int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
Button btDarkMode, btLightMode, btAddWidget;
int widgetMode = R.drawable.rs_dark;
View.OnClickListener darkClickListener = view ->
widgetMode = R.drawable.rs_dark;
btDarkMode.setForeground(new ColorDrawable(ContextCompat.getColor(getBaseContext(), R.color.trans_yellow)));
btLightMode.setForeground(new ColorDrawable(Color.TRANSPARENT));
;
View.OnClickListener lightClickListener = view ->
widgetMode = R.drawable.rs_light;
btLightMode.setForeground(new ColorDrawable(ContextCompat.getColor(getBaseContext(), R.color.trans_yellow)));
btDarkMode.setForeground(new ColorDrawable(Color.TRANSPARENT));
;
View.OnClickListener addWidgetClickListener = v ->
final Context context = MyWidgetConfigureActivity.this;
// When the button is clicked, store the background locally
saveTitlePref(context, mAppWidgetId, widgetMode);
// It is the responsibility of the configuration activity to update the app widget
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
MyWidget.updateAppWidget(context, appWidgetManager, mAppWidgetId);
// Make sure we pass back the diferente appWidgetId
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
;
public MyWidgetConfigureActivity()
super();
// Write the prefix to the SharedPreferences object for this widget
static void saveTitlePref(Context context, int appWidgetId, int modeId)
SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
prefs.putInt(PREF_PREFIX_KEY + appWidgetId, modeId);
prefs.apply();
static Integer loadModePref(Context context, int appWidgetId)
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0);
int modeId = prefs.getInt(PREF_PREFIX_KEY + appWidgetId, 0);
if (modeId == 0)
return R.drawable.rs_dark;
else
return modeId;
static void deleteModePref(Context context, int appWidgetId)
SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
prefs.remove(PREF_PREFIX_KEY + appWidgetId);
prefs.apply();
@Override
public void onCreate(Bundle icicle)
super.onCreate(icicle);
setResult(RESULT_CANCELED);
setContentView(R.layout.my_widget_configure);
btDarkMode = findViewById(R.id.btDarkMode);
btLightMode = findViewById(R.id.btLightMode);
btAddWidget = findViewById(R.id.btAddWidget);
btDarkMode.setOnClickListener(darkClickListener);
btLightMode.setOnClickListener(lightClickListener);
btAddWidget.setOnClickListener(addWidgetClickListener);
// Find the widget id from the intent.
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null)
mAppWidgetId = extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
// If this activity was started with an intent without an app widget ID, finish with an error.
if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID)
finish();
2. Configure el fondo del widget de acuerdo con la referéndum del legatario.
MyWidget.java
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) int modeId = MyWidgetConfigureActivity.loadModePref(context, appWidgetId);
CharSequence loadingText = context.getString(R.string.loading);
// Construct the RemoteViews object
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_widget);
views.setTextViewText(R.id.appwidget_text, loadingText);
views.setInt(R.id.rlWidget, "setBackgroundResource", modeId);
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
Diseño de widget
El diseño de nuestro widget constará de dos instrumentos «TextView» para mostrar la «configuración» del chiste y el «remate» del chiste y un «Timbre» para obtener un nuevo chiste azaroso.
app / res / layout / my_widget.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/rs_dark"
android:padding="@dimen/widget_margin"
android:id="@+id/rlWidget"
android:orientation="tieso"
android:gravity="center"
android:theme="@style/ThemeOverlay.JokesWidget.AppWidgetContainer"><TextView
android:id="@+id/tvJokeSet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:lines="2"
android:gravity="center"
android:text="@string/loading"
android:textColor="@color/white"
android:textSize="16sp"
android:textStyle="bold|italic" />
<TextView
android:id="@+id/tvJokePunch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:lines="2"
android:gravity="center"
android:text="@string/click_for_punchline"
android:textColor="@color/light_blue_900"
android:textSize="16sp"
android:textStyle="bold|italic" />
<TextView
android:theme="@style/Theme.AppCompat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:padding="6dp"
android:background="@drawable/round_frame"
android:textSize="14sp"
android:text="@string/random_joke"
/>
</LinearLayout>
Cree un objeto dibujable round_frame.xml para el fondo de la instinto del capullo del widget
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke
android:width="1dp"
android:color="@color/white" />
<corners android:radius="10dp" />
<padding
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
android:top="5dp" />
</shape>
Manejo de eventos de clic de widget
Para que nuestros widgets respondan,
Al hacer clic en el chiste, se mostrará el chiste, y luego al hacer clic en el capullo «broma aleatoria», se mostrará un nuevo chiste.
Usaremos la intención irresoluto («setOnClickPendingIntent») y anularemos el método onReceive para establecer un «onClickListener» para la «configuración» y el «remate» de los chistes.
MyWidget.java
package com.my.jokeswidget;import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Pair;
import android.widget.RemoteViews;
import java.util.Random;
public class MyWidget extends AppWidgetProvider
private static final String RANDOM_JOKE_CLICKED = "widgetRandomJokeClick";
private static final String PUNCHLINE_CLICKED = "widgetJokePunchlineClick";
private static Pair<String, String> randJoke;
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId)
// Construct the RemoteViews object
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_widget);
CharSequence loadingText = context.getString(R.string.loading);
views.setTextViewText(R.id.tvJokeSet, loadingText);
// Widget drawable background by user selection
int modeId = MyWidgetConfigureActivity.loadModePref(context, appWidgetId);
views.setInt(R.id.rlWidget, "setBackgroundResource", modeId);
// Setup new joke
randJoke = getNewJoke();
views.setTextViewText(R.id.tvJokeSet, randJoke.first);
// Click intents
views.setOnClickPendingIntent(R.id.tvRandomJoke,
getPendingSelfIntent(context, RANDOM_JOKE_CLICKED));
views.setOnClickPendingIntent(R.id.tvJokePunch,
getPendingSelfIntent(context, PUNCHLINE_CLICKED));
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
protected static PendingIntent getPendingSelfIntent(Context context, String action)
Intent intent = new Intent(context, MyWidget.class);
intent.setAction(action);
return PendingIntent.getBroadcast(context, 0, intent, 0);
@Override
public void onReceive(Context context, Intent intent)
super.onReceive(context, intent);
if (RANDOM_JOKE_CLICKED.equals(intent.getAction()))
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.my_widget);
ComponentName jokeWidget = new ComponentName(context, MyWidget.class);
randJoke = getNewJoke();
remoteViews.setTextViewText(R.id.tvJokeSet, randJoke.first);
remoteViews.setTextViewText(R.id.tvJokePunch, context.getString(R.string.click_for_punchline));
appWidgetManager.updateAppWidget(jokeWidget, remoteViews);
if (PUNCHLINE_CLICKED.equals(intent.getAction()))
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.my_widget);
ComponentName jokeWidget = new ComponentName(context, MyWidget.class);
remoteViews.setTextViewText(R.id.tvJokePunch, randJoke.second);
appWidgetManager.updateAppWidget(jokeWidget, remoteViews);
private static Pair<String, String> getNewJoke()
int rand = new Random().nextInt(100);
return new Pair<>("Random joke number: " + rand,
"Punchline number: " + rand);
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds)
updateAppWidget(context, appWidgetManager, appWidgetId);
@Override
public void onDeleted(Context context, int[] appWidgetIds)
// When the user deletes the widget, delete the preference associated with it.
for (int appWidgetId : appWidgetIds)
MyWidgetConfigureActivity.deleteModePref(context, appWidgetId);
@Override
public void onEnabled(Context context)
// Enter relevant functionality for when the first widget is created
@Override
public void onDisabled(Context context)
// Enter relevant functionality for when the last widget is disabled
Antaño de ocurrir al sucesivo tema, depuremos el widget y verifiquemos si todo es ordinario hasta ahora.
Para poder depurar el widget, deberá establecer la actividad de configuración como la actividad principal y la actividad del iniciador.
AndroidManifest.xml
<activity android:name=".MyWidgetConfigureActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
Cliente HTTP con Google Volley
Utilice Google Volley para crear un cliente de refrigerio para nuestra API de broma
- Importar el módulo de Google Volley build.gradle
dependencies
...implementation 'com.android.volley:volley:1.2.0'
...
2. Agregue permisos de red a AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
3. Usaremos la clase singleton de la pan dulce de solicitudes de interceptación y la interfaz de devolución de citación para cuidar las solicitudes GET, y luego crearemos un «JokeClient» para extraer bromas aleatorias de la API.
Cree un nuevo paquete llamado «httpclient» y cree las siguientes clases e interfaces bajo el nuevo paquete,
VolleyRQSingleton.java
package com.my.jokeswidget.httpclient;import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
import android.content.Context;
public class VolleyRQSingleton
private static VolleyRQSingleton mInstance;
private RequestQueue mRequestQueue;
private static Context mCtx;
private VolleyRQSingleton(Context context)
mCtx = context;
mRequestQueue = getRequestQueue();
public static synchronized VolleyRQSingleton getInstance(Context context)
if (mInstance == null)
mInstance = new VolleyRQSingleton(context);
return mInstance;
public RequestQueue getRequestQueue()
if (mRequestQueue == null)
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
return mRequestQueue;
public <T> void addToRequestQueue(Request<T> req)
req.setRetryPolicy(new DefaultRetryPolicy(
5000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
getRequestQueue().add(req);
VolleyCallback.java
package com.my.jokeswidget.httpclient;public interface VolleyCallback
void onSuccess(Boolean isSuccess);
JokeClient.java
package com.my.jokeswidget.httpclient;import android.content.Context;
import android.util.Pair;
import com.android.volley.Request;
import com.android.volley.toolbox.JsonObjectRequest;
import org.json.JSONException;
public class JokesClient
private Pair<String, String> joke;
public void fetchJokeFromServer(Context context, final VolleyCallback callback)
String url = "https://official-joke-api.appspot.com/jokes/random";
JsonObjectRequest jsonRequest = new JsonObjectRequest(Request.Method.GET, url, null,
response ->
try
String setup = (response.getString("setup"));
String punchline = (response.getString("punchline"));
joke = new Pair<>(setup, punchline);
catch (JSONException e)
e.printStackTrace();
callback.onSuccess(true);
,
error ->
System.out.println(error);
callback.onSuccess(false);
);
VolleyRQSingleton.getInstance(context).addToRequestQueue(jsonRequest);
public Pair<String, String> joke()
return joke;
Uso del cliente HTTP
Ahora, usaremos el nuevo cliente HTTP en la clase MyWidget para mostrar chistes aleatorios en nuestro widget,
El sucesivo es el código final de MyWidget.java
package com.my.jokeswidget;import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Pair;
import android.widget.RemoteViews;
import com.my.jokeswidget.httpclient.JokesClient;
public class MyWidget extends AppWidgetProvider
private static final String RANDOM_JOKE_CLICKED = "widgetRandomJokeClick";
private static final String PUNCHLINE_CLICKED = "widgetJokePunchlineClick";
private static Pair<String, String> randJoke;
private static final JokesClient jokesClient = new JokesClient();
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId)
// Construct the RemoteViews object
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_widget);
CharSequence loadingText = context.getString(R.string.loading);
views.setTextViewText(R.id.tvJokeSet, loadingText);
// Widget drawable background by user selection
int modeId = MyWidgetConfigureActivity.loadModePref(context, appWidgetId);
views.setInt(R.id.rlWidget, "setBackgroundResource", modeId);
// Click intents
views.setOnClickPendingIntent(R.id.tvRandomJoke,
getPendingSelfIntent(context, RANDOM_JOKE_CLICKED));
views.setOnClickPendingIntent(R.id.tvJokePunch,
getPendingSelfIntent(context, PUNCHLINE_CLICKED));
// Set a new random joke
ComponentName jokeWidget = new ComponentName(context, MyWidget.class);
setRandomJoke(views, context, appWidgetManager, jokeWidget);
protected static PendingIntent getPendingSelfIntent(Context context, String action)
Intent intent = new Intent(context, MyWidget.class);
intent.setAction(action);
return PendingIntent.getBroadcast(context, 0, intent, 0);
@Override
public void onReceive(Context context, Intent intent)
super.onReceive(context, intent);
if (RANDOM_JOKE_CLICKED.equals(intent.getAction()))
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.my_widget);
ComponentName jokeWidget = new ComponentName(context, MyWidget.class);
setRandomJoke(remoteViews, context, appWidgetManager, jokeWidget);
else if (PUNCHLINE_CLICKED.equals(intent.getAction()))
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.my_widget);
ComponentName jokeWidget = new ComponentName(context, MyWidget.class);
remoteViews.setTextViewText(R.id.tvJokePunch, randJoke.second);
appWidgetManager.updateAppWidget(jokeWidget, remoteViews);
private static void setRandomJoke(RemoteViews remoteViews, Context context,
AppWidgetManager appWidgetManager, ComponentName jokeWidget)
remoteViews.setTextViewText(R.id.tvJokeSet, context.getString(R.string.loading));
remoteViews.setTextViewText(R.id.tvJokePunch, context.getString(R.string.click_for_punchline));
appWidgetManager.updateAppWidget(jokeWidget, remoteViews);
jokesClient.fetchJokeFromServer(context, isSuccess ->
if (!isSuccess)
randJoke = new Pair<>(context.getString(R.string.loading_failed), "");
else
randJoke = jokesClient.joke();
remoteViews.setTextViewText(R.id.tvJokeSet, randJoke.first);
appWidgetManager.updateAppWidget(jokeWidget, remoteViews);
);
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds)
updateAppWidget(context, appWidgetManager, appWidgetId);
@Override
public void onDeleted(Context context, int[] appWidgetIds)
// When the user deletes the widget, delete the preference associated with it.
for (int appWidgetId : appWidgetIds)
MyWidgetConfigureActivity.deleteModePref(context, appWidgetId);
@Override
public void onEnabled(Context context)
// Enter relevant functionality for when the first widget is created
@Override
public void onDisabled(Context context)
// Enter relevant functionality for when the last widget is disabled
Demostración de la imagen del widget
Para cambiar la visualización de la imagen del widget al escoger la imagen del widget desde los widgets del dispositivo, debe copiar la imagen del widget en la carpeta dibujable y luego establecer la nueva imagen en el atributo xml «android: previewImage» del widget.
res / xml / my_widget_info.xml
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:configure="com.my.jokeswidget.MyWidgetConfigureActivity"
android:initialKeyguardLayout="@layout/my_widget"
android:initialLayout="@layout/my_widget"
android:minWidth="250dp"
android:minHeight="80dp"
android:previewImage="@drawable/jokes_img"
android:resizeMode="tieso|horizontal"
android:updatePeriodMillis="86400000"
android:widgetCategory="home_screen|keyguard"/>
Creamos un widget completamente activo usando la pantalla de configuración, que usa preferencias compartidas para achivar las selecciones del legatario.
Usamos google volley para implementar un cliente HTTP, el cliente tiene una pan dulce de solicitudes y una devolución de citación, puedes obtener bromas de la «API oficial de bromas».
Por otra parte, hacemos que el widget responda a la interacción del legatario para mostrar chistes divertidos y nuevos chistes aleatorios.
El código del esquema se encuentra en https://github.com/aviadh314/JokesWidget/tree/master
Hackear.