Generalidades

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

1*frJJYjq0NTKRFtaNFaSjyw

tema

*Yo soy túsestudio de Android, Java para Android (8).

Sin más presentación, comencemos.

Utilice Android Studio para crear widgets predeterminados.

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

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

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

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

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

  1. 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

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

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"/>

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.

LEER  Migración simple a almacenamiento con alcance | Por Mino Mesa | Marzo de 2022

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba